提示:这系列文章会列出关于动态内存常出现的面试题,我会解析每道题中的错误,并改成正确的代码。面试题(一)主要是关于函数调用完毕局部变量会被销毁的问题。
文章目录
面试题一
请思考以下代码,找出错误,并改正
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void GetMemory(char* p)//空指针也是有类型的,它与其他同类型指针的值都不相同
{
p = (char*)malloc(100);//p用来接收str-NULL
}
void Test(void)
{
//一定要将指针变量进行初始化,不然可能会沦为野指针
char* str = NULL;
//调用获取内存函数
GetMemory(str);
//拷贝字符串
strcpy(str, "hello world");
printf(str);//printf("abc");和printf(str)一个道理
}
int main()
{
Test();
return 0;
}
图解错误原因
1.这个代码在vs编译器下运行,会出现出现崩溃的现象;
2.str以值传递的形式给p,我们知道函数内部创建的非静态局部变量只有在函数被调用的时候,才向堆栈中申请临时空间,这里原来p是NULL,然后mallo将开辟的动态内存空间的起始地址传给p,假设起始地址是0x0012ff40,那么指针变量p中现在存的就是这个地址,注意的是函数一旦调用完毕,函数内部申请的栈空间就会被释放,也就是说指针变量p在函数调用完毕后会被销毁,那块内存空间会被操作系统回收,那么起始地址就带不回来了给str了,也就无法完成字符串的拷贝,这个时候在main函数中str为NULL指针,对NULL解引用程序会奔溃。
3.上面可以理解为,malloc动态开辟的内存起始地址是卧底的身份,卧底的身份只有上司p知道,而这时候上司p被杀害了,那么也就没有人会知道这个卧底的身份,即起始地址就无法知道了,这个代码就是错误的,str没有办法获得起始地址,无法进行拷贝。
4.我们在函数GetMemory用malloc开辟了100个字节的动态内存空间,而动态内存是需要我们手动释放的,而在这个代码中,并没有出现free函数对内存进行释放,所以会造成内存泄漏。
正确代码如下
方法1:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* GetMemory(char** p)//二级指针保存的是另一个指针的地址
{
//p存的是指针str的地址
//对p进行解引用,可以拿到str的地址
//然后把malloc开辟的内存的起始地址赋给str的地址
//通过址传递就可以实现字符串拷贝
*p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str);//传址
strcpy(str, "hello world");
printf(str);
free(str);
str=NULL;
}
int main()
{
Test();
return 0;
}
那么如何理解这个代码呢?
我们利用址传递,将str的地址传递过去,二级指针用来接收指针的地址,所以在函数中p中存的是str的地址,解引用p就拿到了str的地址,mallo开辟的动态内存空间的起始地址就可以直接赋给str,这样即使栈区指针变量p被销毁了,也不影响动态内存空间的获取。
方法2:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* GetMemory(char* p)
{
p = (char*)malloc(100);
return p;
}
void Test(void)
{
char* str = NULL;
str=GetMemory(str);
strcpy(str, "hello world");
printf(str);
free(str);
str=NULL;
}
int main()
{
Test();
return 0;
}
我们考虑到如果上司p被杀害就无法知道卧底的身份(动态开辟内存空间的起始地址),那么还有一种方法,就是可以在p被销毁之前,把地址传出去,用str接收(上司被杀害之前,就把卧底的身份告诉另一个人),注意这是返回的是p,而p的类型是char*,所以GetMemory函数的返回类型要改成char*类型。