动态内存管理第一部分解释了什么是动态内存管理,有什么用,以及一些函数,第二部分主要讨论了动态内存在使用的时候会出现一些经典的错误,需要注意。
那么这个第三部分主要讨论一些有关动态内存管理有关的比较经典的笔试题。
题目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;
}
结果:
这段代码会崩溃掉,并且存在内存泄漏的问题。
接下来我们分析一下原因:
从main函数出发,调用Test我们进入Test函数,创建了个char类型指针变量str ,然后调用GetMemory,将str传过去,str值为NULL,那么p传入的参数也为NULL,然后执行malloc动态内存分配:
开辟了一段空间首地址为:12ff21cd并返回给p,那么此时p指向了这段空间,到这里GetMemory的程序执行完毕。回到Test,我们会发现p虽然改了但是GetMemory已经结束p生命周期也已经结束了,并没有改变str的值。
所以此时str的值还是NULL。
str的值为NULL,并不是一个有效的地址。从NULL开始拷贝的时候会从这个地址自增,那必然会访问非法空间,那strcpy肯定是执行不成功的。
strcpy(str,"hello world");
所以这里就会直接崩溃。
崩溃的原因:
所以这里的问题还是因为传递的是str的值而不是str的指针。所以并没有改变str的值,如果想要改变str的值那应该传递指向str的指针。应该是个二级指针。
内存泄漏因为:
str以值的形式传递给p,p是GetMemory函数的形式参数,只在函数内部有效,等函数返回之后,动态开辟内存尚未释放,并且无法找到,所以造成内存泄漏。
要改进怎么改进呢?我们需要用到二级指针,将str地址传进去即可。
void GetMemory(char **p){
*p=(char*) malloc(100);
}
void Test(void){
char *str=NULL;
//传递str的地
GetMemory(&str);
strcpy(str,"hello world");
printf(str);
//释放空间
free(str);
str=NULL;
}
int main(){
Test();
return 0;
}
或者:如果不想用二级指针可以把申请好空间的地址返回去
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;
}
输出结果:
题目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;
}
结果:
随机值,乱码
我们期望输出一个hello world ,接下来我们继续分析一下造成这样的原因:
首先从main函数出发,到Test函数中,创建char类型指针变量str 值为NULL,然后调用GetMemory函数,这个函数中创建了指针数组p赋值为“hello world” 然后return p。
注意这里p确实是返回了一个地址,但是p的作用域或者它所指向的这块空间的生命周期在GetMemory这个函数执行完毕就返回给操作系统了。所以即使把指针p返回去给str,但是那个空间已经不能通过p这个地址访问了。
所以问题还是:
对内存的非法访问(不要随意返回栈空间的地址)
改进:
char *GetMemory(char * p){
p="hello world";
return p;
}
void Test(void){
char *str=NULL;
str=GetMemory(str);
printf(str);
}
int main(){
Test();
return 0;
}
或者给p加上静态修饰
char *GetMemory(void){
static char p[]="hello world";
return p;
}
void Test(void){
char *str=NULL;
str=GetMemory();
printf(str);
}
int main(){
Test();
return 0;
}
改进的方法很多,同第一道类似,也很好理解。
输出结果:
题目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);
}
int main(){
Test();
return 0;
};
输出结果:
这道题相对容易一点,可以看到输出结果也是hello,那有没有问题呢?
答案是有,问题是存在内存泄漏
因为使用malloc开辟空间,用完一定要释放掉。如果没有释放就会导致内存泄漏。
这道题可以结合题目1的改进方案去分析,上面题目1出现的问题这道题已经规避了。所以从输出的角度看是可以输出的。
改进:
void Getmemory(char **p, int num){
*p=(char*) malloc(num);
}
void Test(void){
char *str=NULL;
Getmemory(&str,100);
strcpy(str,"hello");
printf(str);
free(str);
str=NULL;
}
int main(){
Test();
return 0;
};
加上free函数释放即可。
题目4
请问运行Test函数会有什么样的结果?
void Test(void){
char *str=(char *) malloc(100);
strcpy(str,"hello");
free(str);
if (str!=NULL){
strcpy(str,"world");
printf(str);
}
}
int main(){
Test();
return 0;
};
输出结果:
我们看到输出了word,那么这个代码又没有问题呢?答案是有很大的问题
存在着内存非法访问的问题。窜改动态内存区的内容,后果难以预料很危险。
分析一下:
我们从main函数进入Test函数:
开辟了一块100个字节的空间在堆上,然后使用字符串拷贝函数拷贝了字符串“hello”,然后继续执行释放掉了这100个字节的空间。但是str这个指针仍然是指向这段空间的起始位置。
所以这里问题就出现了:空间已经释放还给操作系统了,str已经成为野指针了。你还能够通过原本的指针去访问这段内存,这就是内存非法访问。
虽然通过p访问到了已经被释放的空间,并修改了其中的字符串的值,但是这是种非法操作,因为在实际情况中这块空间很有可能被使用了,但是你还能访问的到,这是非法的。就像谈恋爱的你和对象分手了,但是你却还保留着前任的手机号,还能给她打电话一样,不合理。
那么如何修改呢:
void Test(void){
char *str=(char *) malloc(100);
strcpy(str,"hello");
free(str);//free释放str指向的空间,并不会把str置null
str=NULL;
if (str!=NULL){
strcpy(str,"world");
printf(str);
}
}
这个题考查的点就在于释放完空间后,需要将指针置为NULL。
那么今天的内容就到此为止啦,各位拜拜。
标签:动态内存,管理,void,C语言,char,str,Test,NULL,GetMemory From: https://blog.51cto.com/u_16160587/8680704