四、笔试题
1.请问运行Test函数会有什么样的结果?
void GetMemory(char* p) { p = (char*)malloc(100); } void Test(void) { char* str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } int main() { Test(); return 0; }
结果:
结果运行出错的原因:
(1)
调用GetMemory函数的时候,str的传参为值传递,p是str的临时拷贝,所以在GetMemory函数内部将动态开辟空间的地址存放在p中的时候,不会影响str,所以GetMemory函数返回之后,str中依然是NULL指针。strcpy函数就会调用失败,原因是对NULL指针的解引用操作,程序会崩溃
(2)
GetMemory函数内部malloc申请的空间没有机会释放,造成了内存泄漏
本题目注意点:
(1)注意这个程序想释放也释放不了,无论是在主函数中释放还是在test函数内部。因为一旦返回,就没有人记得动态开辟的空间在哪里,动态开辟空间的地址带不出来。
(2)而这道题的本意是想把100个字节的地址放到str里面,然后把hello world拷贝到str指向的空间里。
改正该题目的错误:
正确修改1:(利用传值)
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; return 0; } int main() { Test(); return 0; }
解释:
(1)这里也是利用传值,但是GetMemory函数返回的是指针,p所指向的是malloc出来的空间,是在堆上,除了函数不销毁这块空间,通过p返回的地址仍然可以找到这块空间。
与原题传值对比:
原题目中的p是函数的形参变量,形参变量是个临时变量,出了函数就销毁了。p销毁了,p这块空间还给操作系统,没人记得malloc申请的空间在哪里。
(2)使用完动态开辟的这块空间用free回收掉
正确修改2:(利用传址)
void GetMemory(char** p) { *p = (char*)malloc(100); } void Test(void) { char* str = NULL; GetMemory(&str); strcpy(str, "hello world"); printf(str); free(str); str = NULL; return 0; } int main() { Test(); return 0; }
解释:
将指针str的地址传给GetMemory函数的形参,str是char类型的是一级指针,它的地址用二级指针**p接收,在GetMemory函数中的p存的就是str,这样的话不通过返回值也能把地址带回来
本题涉及知识点
关于printf打印字符的两种方式:
比如这句代码 char* p=“hehe\n”;
意思不是把字符串hehe\n放到p里面,而是把这个表达式字符串首地址h的地址放到p中
2.请问运行Test函数会有什么样的结果?
char* GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char* str = NULL; str = GetMemory(); printf(str); } int main() { Test(); return 0; }
结果
出错原因:
返回栈空间地址的问题:
GetMemory函数内部创建的数组是临时的,虽然返回了数组的起始地址给str,但是数组的内存出了GetMemory函数就被回收了,而str依然保存了数组的起始地址,这时使用str,str就是野指针
通俗理解本题:
张三开了一间房,告诉李四明天来如家酒店302房间可入住,但是在李四来之前,张三已经把房间退了。等到李四根据张三提供的地址来到302时却无法入住,这间房也不属于李四。
这里的把房间退了相当于将空间还给操作系统,即使你有这个空间的地址信息,而这个空间确不属于你,且不能访问这个空间
教训:局部变量的地址不要随便返回
举例说明:
根据上面的讲解,这里打印的应该是随机值,而这里刚好打印出正确的结果,是怎么回事?难道这样写也正确吗?
答:这样写是错误的。之所以刚好打印出结果10,是因为恰好空间没有被改掉的情况。
当多打印个别的语句时,就可以发现此时*p的值改变成5了。
这里为什么多打印个hehe下面就变了?
答:因为栈帧空间会被覆盖
重点内容:注意区分在函数里什么时候可以返回
char* GetMemory(char* p) { p = (char*)malloc(100); return p; }
3.请问运行Test函数会有什么样的结果?
void GetMemory(char** p, int num) { *p = (char*)malloc(num); } void Test(void) { char* str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); }
错误点:
malloc动态开辟的空间在使用完没有释放掉,造成内存泄漏
改正:
释放指针并将指针置为空
重点
free完之后的指针如果不是空指针,一定要置为空指针,避免野指针问题
4.请问运行Test函数会有什么样的结果?
void Test(void) { char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); if (str != NULL) { strcpy(str, "world"); printf(str); } }
错误点:
(1)free完动态开辟的空间应该及时将指针置空,这里没有置空
(2)str指向的空间已经回收了,因为str没有置为空指针,还能找到刚开辟的那块空间,但是这块空间已经回收给操作系统了,不属于str,不能用,拷贝时形成非法访问,此时的str是野指针
改正:
5.Nice校招笔试题
指出下面哪段程序有问题,并说明问题是什么。
(1)
该题是返回栈空间地址的问题。
如果有人接收了这个函数返回的指针,就会造成野指针的问题。
(2)
指针ptr没有初始化,没有任何指向,里面是随机值。
不知道ptr指向谁,直接对它解引用
对野指针解引用,还把10放进去,非法访问内存。
结语:
本篇内容就到这里啦,关于动态内存管理的内容未完待续,请见下篇博客。如果对大家有帮助的话,希望友友们可以点赞收藏博客,关注后续的学习内容哦!❤️💕💕