当前位置: 首页 > 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个均是动态内存管理的经典题目,希望大家能够消化吸收,感谢大家的观看!

相关文章:

  • 安卓开发用到的设计模式(2)结构型模式
  • Spark MLlib的运维与管理
  • 数据结构第七章(五)-散列表
  • 【算法】力扣体系分类
  • 深入理解计算机内存:物理存储器、地址空间与程序眼中的“内存地址”
  • Spring Boot微服务架构(三):Spring Initializr创建CRM项目
  • 生成图片验证码
  • python中Web框架Flask vs FastAPI 对比分析
  • 自然语言处理核心技术:词向量(Word Embedding)解析
  • PCIE 4.0 vs PCIE 5.0固态硬盘——区别、科普与选购场景全解析
  • ARM笔记-ARM处理器及系统结构
  • 跨平台兼容Setup PDF 编辑器页面合并拆分OCR 识别支持多语言
  • day26CSS-Sass、Stylus、Less
  • 什么是深度学习中的层次分类问题?
  • leetcode 862. 和至少为 K 的最短子数组
  • 软件开发MVC三层架构杂谈
  • C# 异步方法中缺少 `await` 运算符的隐患与解决方案
  • 计算机网络(3)——传输层
  • vue+threeJs 创造镂空管状
  • C# 深入理解类(析构函数和this关键字)
  • 网站建设的相关问题/线上推广渠道有哪些方式
  • 云南省建设厅网站首页/四川成都最新消息
  • 改行做网站/台州seo排名优化
  • 免费的网站域名和空间/域名查询ip爱站网
  • 大丰网站制作/长沙seo网站管理
  • 做网站开专票税钱是多少个点/代运营