从一段经典错误代码说起——关于局部变量指针和函数传参的问题分析

  有一段 精短的 错误代码 ,值得深思下:  
 
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;
}
最后的最后,请一定要注意 释放内存。这个内存的释放 需要在主调函数中去做了。
 
这里 没有遵守 “谁申请,谁释放”的 原则。
 
 
 
 
 
原文地址:https://www.cnblogs.com/Stultz-Lee/p/6732014.html