实例1:
分析下面代码是否有错误?指出错误后果,并修改成正确的代码。
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
分析:在 Test 函数中调用 GetMemory(str); 后,实参 str 并未发生改变,依然是 NULL。在C语言中,形参值的改变并不能影响实参值。因为实参和形参是属于两个不同的存储空间,实参 —> 形参 是值传递,即它是一次内存拷贝的过程。
结果:当程序运行到 strcpy(str, "hello world"); 时,会产生错误,程序崩溃。
修改:可以使用二级指针的形式,正确代码如下:
void GetMemory(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str=NULL;
GetMemory(&str, 100);
strcpy(str,"hello world");
printf(str);
free(str);
}
/**分析如下:
1)指针变量 str 本身占用一个4字节的存储单元,这个存储单元自己也是有一个地址值的,用 &str 来表示
2)将 &str 作为实参传递给形参 p,那么二级指针 p 就指向了指针变量 str。二级指针就是指向指针的指针
3)*p 运算就是指针 p 指向的指针变量 str 的值,然后将malloc动态申请的内存的首地址赋值给*p,就是赋
值给指针变量str,也就是说str存放的就是这个动态内存空间的首地址,亦即指针str指向了这块动态分配的内
存空间。
*/
<Tips> 在函数参数中使用二级指针的方式,可以把在一个函数内使用 malloc 动态分配的内存空间的首地址带出函数来,然后在其他地方就可以使用这片动态申请的内存空间了。
实例2:
分析下面代码是否有错误?指出错误后果,并修改成正确的代码。
char* GetMemory(void)
{
char p[] = "hello world"; //用字符串常量来初始化数组的内存空间
//printf("address p: %#p\n", p); //address p: 0X000000000061FDA4
return p; //编译器发出警告: warning: function returns address of
//local variable
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
//printf("address str: %#p\n", str); //address str: 0000000000000000
printf(str);
}
分析:在 GetMemory() 函数中,试图返回数组变量 p 的首地址,但是数组p是函数 GetMemory 内的局部变量,而局部变量是存放在进程空间的栈区中的,当 GetMemory 函数执行结束时,为数组p分配的内存空间也会随之被自动释放掉。因此,在Test函数中,并不能输出正确的结果。
结果:输出的结果为空或者是乱码,这取决于编译器的编译处理。
修改:修改方式有两种,具体代码如下:
//方式1-将数组改成指针,然后返回指针变量(最简单)
char* GetMemory(void)
{
char *p = "hello world";
return p;
}
//方式2-使用二级指针的方式
char* GetMemory(char **p)
{
*p = "hello world";
return *p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory(&str);
printf(str);
}
<Tips> 在函数内,不要用 return 语句返回指向“栈内存”的指针,因为栈内存在函数结束运行时将会被系统自动释放掉。
实例3:
分析下面代码是否有错误?指出错误后果,并修改成正确的代码。
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello world");
printf(str);
}
分析:在 GetMemory 函数中,使用 malloc 动态分配了内存,但是没有释放。在 Test 函数中可以输出 "hello world",但是会存在内存泄漏的风险。
修改:在 Test 函数中添加一条语句:free(str);
<Tips> 使用 malloc 或者 calloc 动态分配内存空间时,使用完后,一定要记得使用 free 将动态申请的内存释放掉。
实例4:
分析下面代码是否有错误?指出错误后果,并修改成正确的代码。
void Test(void)
{
char *str = (char *)malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
分析:篡改动态内存区的内容,后果难以预料,非常危险。因为 free(str) 后,str指针就变成野指针了。if(str != NULL) 这个条件表达式是不起作用的,因此 if 语句块一定会被执行。
修改:在 free(str); 语句下面添加一条语句:str = NULL;
<Tips> free 掉一个指针后,最好马上将其赋值为NULL,不然容易导致野指针的出现。