当前位置: 首页 > news >正文

C语言 动态内存管理(4)

在前面关于动态内存管理的三篇文章中,已经将动态内存管理的内容全部讲完,为了能够更好的理

解动态内存管理和三个函数,有下面四个经典的笔试题,接下来跟着我一起来看看吧!

1.题目1:

void GetMemory(char *p) {p = (char *)malloc(100);
}
void Test(void) {char *str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}

问题:请问运行Test函数会有什么样的结果?

答案是:  程序运行崩溃

1. 函数调用与参数传递机制

在C语言中,函数参数传递是值传递 。 Test 函数中调用 GetMemory(str) 时, str 的值是 NULL  )

被传递给 GetMemory 函数的形参 p  。这意味着 p 和 str 是两个不同的指针变量,只是初始时 p 获

得了 str 的值。

2. 内存分配操作

在 GetMemory 函数内部,执行 p = (char *)malloc(100);  ,这确实通过 malloc 函数在堆区分配了

100字节的内存空间,并让指针 p 指向该空间。但是,这个 p 是 GetMemory 函数的局部变量,

当 GetMemory 函数执行结束返回时, p 的生命周期结束,其作用域消失。

3. 后续操作问题

回到 Test 函数, str 的值并没有因为 GetMemory 函数内部对 p 的操作而改变,它仍是 NULL  。

当执行 strcpy(str, "hello world"); 时, strcpy 函数的作用是将字符串 "hello world" 复制到 str 指向

的内存区域。由于 str 是 NULL  ,也就是试图向地址为0的无效内存区域写入数据,这在操作系统

中是不允许的,会触发段错误(Segmentation Fault),导致程序崩溃 。

所以,运行 Test 函数的结果是程序崩溃。

2.题目2:

char *GetMemory(void) {char p[] = "hello world";return p;
}
void Test(void) {char *str = NULL;str = GetMemory();printf(str);
}

问题:请问运行Test函数会有什么样的结果?

答案是:  运行 Test 函数时,会输出乱码或导致程序错误

1. 数组的存储区域与生命周期

GetMemory 函数中定义的数组 p[ ] 是局部变量,存储在栈区。栈区内存的特点是:函数执行结束

后,栈帧会被系统自动释放,其中的局部变量(包括数组 p )的内存空间会被回收,不再有效。

2. 指针返回的本质问题

函数返回 p 时,返回的是数组 p 的首元素地址(即栈区中数组的起始地址)。但当 GetMemory 函

数执行完毕后,栈区的 p 数组内存已被释放。此时, Test 函数中的 str 虽然获取了这个地址,但

该地址指向的内存已经是无效区域(俗称“野指针”)。

3. 访问无效内存的后果

执行 printf(str) 时,程序会从 str 指向的地址读取数据。由于该地址对应的栈内存已被释放,其中

的数据可能已被其他程序或系统操作覆盖,因此会输出不确定的乱码或引发程序异常(如访问违规

错误)。
 
关键结论:
 
运行 Test 函数时,会输出乱码或导致程序错误,原因是返回了栈区局部变量的地址,该地址在函

数结束后已失效。

核心教训:永远不要返回局部变量(栈内存)的地址,除非用 static 修饰或动态分配内存(堆

区)。

3.题目3:

void GetMemory(char **p, int num) {*p = (char *)malloc(num);
}
void Test(void) {char *str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}

问题:请问运行Test函数会有什么样的结果?

答案是:  会正常输出 hello ,但代码存在内存泄漏风险。

1. 二级指针与内存分配

-  GetMemory 函数的参数是二级指针 char **p ,接收的是 Test 函数中 str 指针的地址( &str )。

- 通过 *p = malloc(num) ,直接修改了 str 本身的指向,使其指向堆区分配的 100字节 内存空间。

- 此时, str 不再是 NULL ,而是指向有效且未被释放的堆内存。

2. 数据写入与输出

-  strcpy(str, "hello") 将字符串写入 str 指向的堆内存,操作合法(堆内存未被释放)。

-  printf(str) 从有效内存中读取数据,因此会正常输出 hello 。

3. 潜在问题:内存泄漏

- 虽然程序运行时结果正确,但 malloc 分配的堆内存未被释放。

- 每次调用 malloc 后,必须用 free(str) 释放内存,否则会导致内存泄漏(累计占用内存无法回

收)。
 
关键结论:

运行 Test 函数时,会正常输出 hello ,但代码存在内存泄漏风险。

核心要点:

 - 二级指针可用于修改原始指针的指向,实现对指针本身的“双向通信”。

- 动态分配的内存必须显式释放( free ),否则会导致资源浪费。

4.题目4:

void Test(void) {char *str = (char *)malloc(100);strcpy(str, "hello");free(str);if (str != NULL) {strcpy(str, "world");printf(str);}
}

问题:请问运行Test函数会有什么样的结果?

答案是:  程序崩溃

1. 内存分配与数据写入:

- 首先通过 malloc(100) 在堆区分配了100字节的内存空间,并让指针 str 指向该空间。

- 接着使用 strcpy(str, "hello"); 将字符串 "hello" 复制到 str 指向的内存区域,此时内存区域内容

为 "hello"  。

2. 内存释放操作:

- 执行 free(str); 后, str 所指向的堆内存被释放,归还给系统。虽然 free 操作释放了内存,

但 str 本身的值(地址)并不会自动变为 NULL  ,它仍然指向之前已释放的那块内存区域,此

时 str 成为了“野指针” 。

3. 条件判断与后续操作问题:

-  if (str != NULL) 这个判断条件会成立,因为 str 的值不是 NULL  。

- 然后执行 strcpy(str, "world");  ,由于 str 指向的内存已经被释放,再次向该地址写入数据属于

非法操作,会导致程序出现未定义行为,可能引发程序崩溃、数据损坏等问题。

- 执行 printf(str); 同样是访问已释放的无效内存区域,也会导致未定义行为。
 
关键结论:
 
运行 Test 函数会出现未定义行为,很可能导致程序崩溃 ,原因是使用了已释放内存的“野指针”进

行数据写入和读取操作。

以上4个均是动态内存管理的经典题目,希望大家能够消化吸收,感谢大家的观看!

http://www.xdnf.cn/news/629623.html

相关文章:

  • SQL SERVER常用聚合函数整理及示例
  • 7.1查找的基本概念
  • 【samba和nfs的搭建】
  • MCP与AI模型的多语言支持:让人工智能更懂世界
  • 关于spring @Bean里调用其他产生bean的方法
  • Mybatis(2)
  • Oracle基础知识(四)
  • OpenLayers 加载测量控件
  • 网络安全零基础培训 JavaScript基础知识点
  • 传奇各种怪物一览/图像/爆率/产出/刷新地/刷新时间/刷怪时间
  • Compose 中的 LaunchedEffect
  • 深入了解linux系统—— 操作系统的路径缓冲与链接机制
  • 真实案例拆解:智能AI客服系统中的两类缓存协同
  • 由浮点数的位级表示判断大小关系
  • 人工智能100问☞第31问:如何评估一个AI模型的性能?
  • 【MySQL】索引
  • 【动态规划】P12223 [蓝桥杯 2023 国 Java B] 非对称二叉树|普及+
  • python打卡day35@浙大疏锦行
  • 【笔记】OpenCV的学习(未完)
  • CodeBuddy 实现图片转素描手绘工具
  • springboot中各模块间实现bean之间互相调用(service以及自定义的bean)
  • 符合Python风格的对象(使用 __slots__ 类属性节省空间)
  • 搜索二叉树
  • 开盘啦 APP 抓包 逆向分析
  • 从有线到无线:PLC通讯“剪断“最后一根线!
  • MQTT-排它订阅
  • STM32F103 HAL多实例通用USART驱动 - 高效DMA+RingBuffer方案,量产级工程模板
  • python训练营第33天
  • Lesson 22 A glass envelope
  • HJ14 字符串排序【牛客网】