第二篇内容为大家详细剖析关于动态内存管理的几个经典笔试题
目录
- 四、笔试题
- 1.请问运行Test函数会有什么样的结果?
- 结果:
- 结果运行出错的原因:
- 本题目注意点:
- 改正该题目的错误:
- 正确修改1:(利用传值)
- 正确修改2:(利用传址)
- 本题涉及知识点
- 2.请问运行Test函数会有什么样的结果?
- 结果
- 出错原因:
- 通俗理解本题:
- 教训:局部变量的地址不要随便返回
- 重点内容:注意区分在函数里什么时候可以返回
- 3.请问运行Test函数会有什么样的结果?
- 错误点:
- 改正:
- 重点
- 4.请问运行Test函数会有什么样的结果?
- 错误点:
- 改正:
- 5.Nice校招笔试题
- (1)
- (2)
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指向的空间里。
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回收掉
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中
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;
}
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完之后的指针如果不是空指针,一定要置为空指针,避免野指针问题
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是野指针
指出下面哪段程序有问题,并说明问题是什么。
该题是返回栈空间地址的问题。
如果有人接收了这个函数返回的指针,就会造成野指针的问题。
指针ptr没有初始化,没有任何指向,里面是随机值。
不知道ptr指向谁,直接对它解引用
对野指针解引用,还把10放进去,非法访问内存。
结语:
本篇内容就到这里啦,关于动态内存管理的内容未完待续,请见下篇博客。如果对大家有帮助的话,希望友友们可以点赞收藏博客,关注后续的学习内容哦!❤️