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