4.6.2.2 无法把指针变量本身传递给一个函数
这很像孙悟空拔下一根猴毛变成自己的样子去忽悠小妖怪。所以fun 函数实际运行时,用到的都是_p2 这个变量而非p2 本身。如此,我们看下面的例子:
void GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
}
intmain()
{
char *str = NULL;
GetMemory(str,10);
strcpy(str,”hello”);
free(str);//free 并没有起作用,内存泄漏
return 0;
}
在运行strcpy(str,”hello”)语句的时候发生错误。这时候观察str 的值,发现仍然为NULL。也就是说str 本身并没有改变,我们malloc 的内存的地址并没有赋给str,而是赋给了_str。而这个_str 是编译器自动分配和回收的,我们根本就无法使用。所以想这样获取一块内存是不行的。那怎么办? 两个办法:
第一:用return。
char * GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
return p;
}
intmain()
{
char *str = NULL;
str = GetMemory(str,10);
strcpy(str,”hello”);
free(str);
return 0;
}
这个方法简单,容易理解。
第二:用二级指针。
void GetMemory(char ** p, int num)
{
*p = (char *)malloc(num*sizeof(char));
return p;
}
intmain()
{
char *str = NULL;
GetMemory(&str,10);
strcpy(str,”hello”);
free(str);
return 0;
}
注意,这里的参数是&str 而非str。这样的话传递过去的是str 的地址,是一个值。在函数内部,用钥匙(“*”)来开锁:*(&str),其值就是str。所以malloc 分配的内存地址是真正赋值给了str 本身。
另外关于malloc 和free 的具体用法,内存管理那章有详细讨论。
4.6.3 二维数组参数与二维指针参数
前面详细分析了二维数组与二维指针,那它们作为参数时与不作为参数时又有什么区别呢?看例子:
void fun(char a[3][4]);
我们按照上面的分析,完全可以把a[3][4]理解为一个一维数组a[3],其每个元素都是一个含有4 个char 类型数据的数组。上面的规则,“C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。”在这里同样适用,也就是说我们可以把这个函数声明改写为:
void fun(char (*p)[4]);
这里的括号绝对不能省略,这样才能保证编译器把p 解析为一个指向包含4 个char 类型数据元素的数组,即一维数组a[3]的元素。
同样,作为参数时,一维数组“[]”号内的数字完全可以省略:
void fun(char a[ ][4]);
不过第二维的维数却不可省略,想想为什么不可以省略?
注意:如果把上面提到的声明void fun(char (*p)[4])中的括号去掉之后,声明“void f un(char *p[4])”可以改写成:
void fun(char **p);
这是因为参数*p[4],对于p 来说,它是一个包含4 个指针的一维数组,同样把这个一维数组也改写为指针的形式,那就得到上面的写法。
上面讨论了这么多,那我们把二维数组参数和二维指针参数的等效关系整理一下:
这里需要注意的是:C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。这条规则并不是递归的,也就是说只有一维数组才是如此,当数组超过一维时,将第一维改写为指向数组首元素首地址的指针之后,后面的维再也不可改写。比如:a[3][4][5]作为参数时可以被改写为(*p)[4][5]。至于超过二维的数组和超过二级的指针,由于本身很少使用,而且按照上面的分析方法也能很好的理解,这里就不再详细讨论。有兴趣的可以好好研究研究。