从一段经典错误代码说起——关于局部变量指针和函数传参的问题分析
有一段 精短的 错误代码 ,值得深思下:
voidGetMemory(char*p,int num)
{
p =(char*)malloc(sizeof(char)* num);
}
voidTest(void)
{
char*str = NULL;
GetMemory(str,100);
strcpy(str,"hello");
}
这段代码 明眼人一看就知道 有错误,可是真让他说原因,可能一时也说不明白,最直观地看法是:没有释放内存,造成了内存泄漏,但是 还远不止 如此。
这个代码的深层错误原因 和 经典的 错误 swap()函数一样:
void swap(int a,int b)
{
int c =0;
c = a;
a = b;
b =c;
}
看了这两个类比的 例子,你应该就知道原因了,书面表达来说就是:
函数参数其实都是 传值的,传值就意为着:参数 进入到函数体后 ,参与运算的 都是 这个 实际参数 的一份拷贝,对实参并没有起到 改变的作用,因此 不管是 GetMemory 还是swap,操作的 都是实参的 一份拷贝,回到了主调函数中,对实际参数根本没有起到 变动的作用,
因此,GetMemory执行完毕后,str 的值仍然是NULL,swap后 ,a,b还是 a,b。
为了达到 函数的功能 要求,你可能也知道 该怎么做,那就是 传 指针。传指针 并没有改变函数参数 是传值的 属性,但是 我们却可以通过操作指针即实参的地址 来达到改变 实参本身的目的。
因此 GetMemory 可以写成
voidGetMemory(char**p,int num)
{
*p =(char*)malloc(sizeof(char)* num);
}
调用时 将str的指针 传进来 就可以了。
同理 :swap 也要改写成:
void swap(int*a,int*b)
{
int c =0;
c =*a;
*a =*b;
*b =c;
}
传参数 时 需要 给出 a和b的 地址即可。
回到 标题,局部变量指针 实际上 有两种情形:
1.指向 堆区 ,如 :char *ptr = (char*)malloc(20);
2.指向 栈区 ,如,char ptr[] = "hello world";
当以上代码 写在函数中时,只有 指向堆区的 指针可以合理合法的作为 返回值,而指向 栈区的指针的内容 因为 函数的返回 而被回收,即使将该指针作为返回值,也达不到预期的效果。这是 堆区和栈区的本质区别。
因此,最终的,我们可以将 GetMemory函数 无二级指针化,即:
char*GetMemory(int num)
{
char*p = NULL;
p =(char*)malloc(sizeof(char)* num);
return p;
}
这里 没有遵守 “谁申请,谁释放”的 原则。