C语言传递指针参数的陷阱
简介
师兄发来一段代码问代码是否有误。
1 | void Malloc1(char *p, int num) { |
基础知识
二级指针
指针是一个变量,它的值是一个地址,地址指向为一个对应类型的变量。
赋值符号=
, 左边是一个地址,右边是赋给该地址的值。变量是一个地址的别名。
&符号
int &a =b
a是b的引用,是b的别名,此时&
用来表示引用
单独使用时,取地址符&
,用来获得一个变量的地址。&a
为变量a
的地址
*符号
int *a = &b
a是指向b的指针,此时*
表示指针变量
解引用符*
,用来得到指针指向的内存地址的值。*a
为a这个指针指向的对象的值。
如下程序:
1 | void printAddr() { |
对应输出为
1 | the address of a is:000000000062FDE4 |
最后建议每一次指针都加一个p,每解引用一次,也就是加一个*
,对应划掉一个p,也就得到对该指针解引用得到的变量值。这样只是方便快速推导,因为实际的理解比较绕。
传参的几种方式
函数被调用的时候,用实参的值来初始化形参。
1 | void func1(int a){//a为形参 |
在调用的时候可以理解为有个int a = b
的过程。
按值传递
1 | void func1(int a){//a为形参 |
使用实参的值来进行初始化,形参在被调用函数中值得变化不会影响实参。
按指针传递
指针形参也是一种值的传递,只是因为传递的是一个对象的地址,因此可以通过解引用符*
来对该对象直接进行修改,所以在被调用函数中可以直接根据地址来进行操作,从而使得该地址的变量被修改。
1 | void func1(int *a){//a为形参 |
指针形参,如果要修改外面的实参指向的变量值,就必须使用*
来进行解引用。
按引用传递
1 | void func1(int &a){//a为形参 |
使用引用传参,可以避免值的拷贝。指针形参和引用传递,都能够修改实参的值是因为都是根据地址来进行操作的。
错误的原因
所以经过上面的复习,错误的原因就是:
1 | void Malloc1(char *p, int num) { |
整个函数的作用只是在形参初始的时候把s指向的地址也就是NULL,即0这个地址赋值给了p指针。之后又把p的值从0,变为了malloc
函数分配的一个地址。在这个函数中只有p这个形参的指向地址发生了改变,实参s指针并未发生改变。
所以经过这个函数的调用后,s指针存放的地址依然是0,导致后面的strcpy(s, "hello");
是对空指针进行操作,所以报错。
正确的写法
首先明确这段代码的意图是想让s指针这个变量的值在被调用函数中发生变化,因为需要修改实参变量的值,所以需要传入实参的地址,指向指针的地址也就是一个二级指针。所以形参应该是一个二级指针,在被调用函数中使用*
来对地址进行解引用。
1 | void Malloc(char **p, int num) { |
总结
- 不是语法的错误,最开始以为没有错误,是因为错误地以为指针作为形参就修改了外层实参的值。但是实际上不一定,比如不加解引用符号
*
则不会修改。而就算加了解引用符号,修改的也是外层实参指向的变量的值。所以一级指针无法完成修改外层指针这个变量本身的值的目的,需要使用二级指针。 - 修改实参的值,需要传入实参的地址。修改指针的值,需要传入指针变量的地址。