指针参数在函数中不能改变指针指向的原因和解决方法
原始问题
实现二叉树排序,需要使用一个数组构建一个二叉排序树,最开始写的代码如下:
struct BST{ int number; //保存数组元素的值 struct BST* left; struct BST* right; }; void insertBST(BST* tree, int v) { if (tree == NULL) { tree = new BST; tree->left=tree->right=NULL; tree->number=v; return; } if (v < tree->number) insertBST(tree->left, v); else insertBST(tree->right, v); } void createBST(BST* tree, int a[], int n) { tree = NULL; for (int i=0; i<n; i++) insertBST(tree, a[i]); }
结果发现每次进入insertBST的时候,tree指针都是空的。用简单的例子做实验,发现如果一个指针为NULL,那么在函数中指向一个对象,函数返回后指针依旧为空,即函数中更改指针指向的对象无效。
void tmp(int* a) { if (a==NULL) { a=new int; *a=200; } else *a=100; } int main() { int* b=NULL; tmp(b); if (b == NULL) cout << "b is null"; else cout << *b << " "; }
运行上述代码,发现输出“b is null”,即b指针在tmp函数中被赋值无效。
而如果b指针预先执行一个对象,那在tmp函数中改变其值是有效的。即在int*b = NULL下边加上一行b=new int;的代码,可以输出改变后值为100.
如果希望在函数中为指针复制,可以使用额外一层的指针,具体代码如下:
void tmp(int** a) { if (*a==NULL) { *a=new int; **a=200; } else **a=100; } int main() { int* b=NULL; tmp(&b); if (b == NULL) cout << "b is null"; else cout << *b << " "; }
此时可以输出200.
问题分析
为NULL的指针在函数中指向一个对象无效。而初始化之后可以改变指向对象的值。
指针本身也是一个值,它的值是所指向对象的地址。指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
回到上述例子上,指针在传递时,相当于,在被调函数中,申明了一个int*的变量,其值就是传递进来的int*.即调用tmp(b)时,会执行 int* c=int*b; 那么在被调函数的堆栈中修改局部变量int*c是 当然也是不会影响到int*b的地址。
但是当b指针不为NULL的时候,改变b指针的值会有效,这是因为局部变量c和参数b指向的是同一个对象,针对这个地址的修改在函数返回式依然有效。
解决方法
双重星号的方式是有效的。
此时传递的不再是b指针的值,而是通过&b传递的b指针的地址,此时是参数是引用传递的,引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。因此可以在函数中改变b指针的值,也就是指针指向的对象。
可以看出,对指针取地址,函数定义的时候使用双重星号的作用不仅仅是为指针赋初始值,还可以在函数中改变指针指向哪个对象,而在普通的指针参数传递中,函数只能改变指针指向对象的值,不能改变指针具体指向哪个对象。
说到底,出现该问题的原因还是对指针的理解以及对参数传递方式的理解不够到位。
完整的二叉树排序代码见上一篇博客:http://www.cnblogs.com/zhaoshuai1215/p/3448154.html