1.指针与引用
C++引入了参数引用机制,从而在一定程度上避免了对指针过多的纠缠,不过引用的机制掩盖了初学者对于指针的迷茫,但是这对真正想做技术来说不一定是好事。
下面是C++的一个引用调用:
void f(int ¶meter )
{
parameter =3;
}
int main()
{
int a=0;
f(a);
printf("%d",a);
...... //代码略
}
最后打印出来的是3,而不是0,这就是C++引用调用的用处,在主函数定义的一个变量,如若我们需要在函数体内对它进行更改,我们就用引用调用,这样在函数体内对于parameter的操作我们就可以理解为是直接对主函数变量a的操作了。如果我们不采用引用调用的话,实际上的内存中的操作如下图所示:
当主函数的变量传入到调用函数中时,调用函数将会“拷贝”出一个a的副本来,这样我们直接在调用函数中的操作,实际上是对拷贝的一份操作,比如parameter=3 只是对另一个"a"进行操作,实际上在主函数的a根本没有任何改变,这也就是我们教科书上花很大篇幅教导我们那个Swap(int a,int b)不成功的原因。
但是在C中,我们没有调用引用的概念,不过借助于指针,我们完全可以模拟出引用调用的操作。那么在纯C中,我们只需要按照这个思路来写出
a是主函数定义的一个变量,我们通过把它的地址传给函数体内定义的指针,使指针指向该地址,那么通过改变函数体指针的值,我们就可以改变主函数的值了。
void f(int *p)
{
*p=3;
}
void main()
{
int a=2;
f(&a);
}
注意上面的f(&a),表示把地址传给函数体内的指针,那么指针就是指向了主函数的变量a所在内存,改变p实质就改变了a的值了,通过这种办法我们就解决C++的引用在C中实现的问题了。
接着说指针用作引用参数传递的问题。为了解释这个问题,我们来看看这个例子。这个是我做数据结构时遇到的一个一直悬而未决的问题(呵呵):
问题开始前,我们先定义一个结构体:
int data;
struct node *Lchild,*Rchild;
}node;
很简单的一个结构体,学过数据结构的应该很快能看出这是个树的结点。我们的目的是在主函数定义一个node指针,初始值为空。然后我们再传到一个函数内分配空间:
int main()
{
node *root=NULL;
maketree(root);
...........
}
//函数
void maketree(node * &root)
{
root=(node *)malloc(sizeof(node));
}
代码也很简单,主要意图是想在maketree中建立一棵树,通过C++的引用调用,我们就可以把root这个指针指向一棵按照要求建立的一棵树。不过这是借用的C++中的引用,如果我们要在纯C中实现该功能呢?要在主函数定义一个指针然后在其它函数中修改该指针的值,并且要保证修改后主函数指针也跟着改变。
int main()
{
node *root=NULL;
maketree(root);
...........
}
//函数
void maketree(node * root)
{
root=(node *)malloc(sizeof(node));
}
注意函数maketree中参数的变化,我们去掉了"&"这个引用调用的符号。同样是通过指针调用,比对一下小议指针(一)中的程序,仅仅是在主函数中差了一个&,注意这里的'&'符号是一个取地址符号,跟上面的C++中的'&'完全不一样,但是这样最后我们在主函数得到的指针值是个非法的值,仍然不可访问!
回想一下函数调用的原理:
主函数传递指针给maketree函数,maketree函数拷贝了原指针。
接下来就明了了,看看这个:
函数体内拷贝root的指针p开始是NULL,但是现在不同啦,我们为他开辟了一个空间,但是root呢,仍然是为空。(我们根本没有动过root ! 操作都在这个貌似root的拷贝函数p上)
回忆下小议指针一中我们的思路:
注意上图p是指向root的一个指针,这样我们想对root进行操作,只需要对p动动刀即可,这样我们在函数体内的操作间接就是对root操作了。
int main()
{
node *root=NULL;
maketree(&root);
...........
}
//函数
void maketree(node * *root)
{
*root=(node *)malloc(sizeof(node));
}
分析下上面的改动,
第一 主函数中的root改为&root,表示把root的地址传递给函数
第二 函数参数中root为为二级指针,因为主函数中root是一级指针,现在我们需要一个指向一级指针的指针,自然是二级指针了
第三 函数体内root改为*root,maketree中的root就是上图中的p,而我们如果要对root操作,自然要取p的指向,例如*p=a,如果我们要对a借助p操作,我们是写p,还是写*p呢?