可能大家在刚开始学习C的时候都被一些教材误导,认为C中有值传递和地址传递两种方式。其实只有值传递一种,无论函数以什么形式进行传递,其实传递的都只是参数的一份拷贝!
举个简单的例子,一个改变某个整型变量参数的函数
int change_value(int *pChange, int val)
{
*pChange = val;
return *pChange;
}
那么pChange所指向地址内容的指改变了吗?确实改变了。
因此一些教材就认为该方式是传址,其实过程应该是这样
比如调用的 时候有如下:
int a = 0x55aa;
change_value(&a, 0xaa55);
这样a的值改变了,真正的情况是参数传递后,编译器做了一个参数拷贝过程,比如声明一个整型指针pCopy = &a;
这样,pCopy只是传进来参数&a的一份拷贝,但是他们都指向了a的地址,因此用这种方法可以改变a 的值。
那么现在有个问题,通过传递一个指针,可以改变该指针所指向地址的内容,那么如何改变指针本身呢?即如何改变指针的指向?
先看一个错误的例子
void change_ptr(void *ptr, void *dest)
{
ptr = dest;
}
如果是这样,能达到改变指针本身的目的吗?
如果你有迷惑,可以这样看,把类型去掉,把参数ptr传递进去,能改变ptr吗?
显然不能!
那该怎么做才能改变ptr呢?
既然ptr也是一个变量,我要在函数内部改变它,那么就传递一个指向ptr的指针!也就是二级指针!
正确的函数形式如下
void change_ptr(void **ptr, void *dest)
{
*ptr = dest;
}
其实指针也是一个变量,我们如果要改变它,必须找到它在内存中的地址,也就是指针的地址。
当然,也可以通过另一种方式,只不过该方式需要绑定调用者的行为
void *change_ptr(void *ptr, void *dest)
{
return (ptr = dest);
}
这样在调用的时候必须做如下形式的调用
char *string;
string = change_ptr(string, dest);
否则string本身是不会改变的!但是这样做意义不大,不如直接来个string = dest方便。
能用到在函数内部改变指针指向的地方通常是:一个函数完成某项功能后,得到一个指针,需要把这个指针以参数的 形式返回。
比如: foo(..., type **value);
在使用的时候通常这样:
type *val = NULL;
foo(..., &val);
这样之后,val指针就指向了正确的位置。
这里申明一点,想去改变一个变量本身的地址是不可能的,无论你怎么做,在声明(定义)变量的时候,它的地址就由编译器决定好了。
比如这里,你能修改val本身的地址吗?这就如同你能修改常量吗?
另外,如果我们想改变结构体中某个变量,需要传递一个指向该结构体的指针!
这样,不管你想改变的是结构体中的成员是一个普通变量还是一个指针,都是可以改变的,因为通过该结构体指针,可以获得该结构体(也是该结构体中第一个成员)的地址,进而也就知道了结构体中任一成员的地址,因此对于结构体内容,想怎么改变就怎么改变!
比如
typedef struct xxx {
int *head;
}xxx_t;
xxx_t my_struct;
void change(xxx_t *dst, void *src)
{
dst->head = str; //dst->head已经改变!
}
调用时
change(&my_struct, src);
这时my_struct.head本身已经改变了。