指针参数在函数中不能改变指针指向的原因和解决方法
2013-12-05 10:05
375 查看
原始问题
实现二叉树排序,需要使用一个数组构建一个二叉排序树,最开始写的代码如下:
结果发现每次进入insertBST的时候,tree指针都是空的。用简单的例子做实验,发现如果一个指针为NULL,那么在函数中指向一个对象,函数返回后指针依旧为空,即函数中更改指针指向的对象无效。
运行上述代码,发现输出“b is null”,即b指针在tmp函数中被赋值无效。
而如果b指针预先执行一个对象,那在tmp函数中改变其值是有效的。即在int*b = NULL下边加上一行b=new int;的代码,可以输出改变后值为100.
如果希望在函数中为指针复制,可以使用额外一层的指针,具体代码如下:
此时可以输出200.
问题分析
为NULL的指针在函数中指向一个对象无效。而初始化之后可以改变指向对象的值。
指针本身也是一个值,它的值是所指向对象的地址。指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
回到上述例子上,指针在传递时,相当于,在被调函数中,申明了一个int*的变量,其值就是传递进来的int*.即调用tmp(b)时,会执行 int* c=int*b; 那么在被调函数的堆栈中修改局部变量int*c是 当然也是不会影响到int*b的地址。
但是当b指针不为NULL的时候,改变b指针的值会有效,这是因为局部变量c和参数b指向的是同一个对象,针对这个地址的修改在函数返回式依然有效。
解决方法
双重星号的方式是有效的。
此时传递的不再是b指针的值,而是通过&b传递的b指针的地址,此时是参数是引用传递的,引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。因此可以在函数中改变b指针的值,也就是指针指向的对象。
可以看出,对指针取地址,函数定义的时候使用双重星号的作用不仅仅是为指针赋初始值,还可以在函数中改变指针指向哪个对象,而在普通的指针参数传递中,函数只能改变指针指向对象的值,不能改变指针具体指向哪个对象。
说到底,出现该问题的原因还是对指针的理解以及对参数传递方式的理解不够到位。
完整的二叉树排序代码见上一篇博客:/article/5623677.html
实现二叉树排序,需要使用一个数组构建一个二叉排序树,最开始写的代码如下:
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指针的值,也就是指针指向的对象。
可以看出,对指针取地址,函数定义的时候使用双重星号的作用不仅仅是为指针赋初始值,还可以在函数中改变指针指向哪个对象,而在普通的指针参数传递中,函数只能改变指针指向对象的值,不能改变指针具体指向哪个对象。
说到底,出现该问题的原因还是对指针的理解以及对参数传递方式的理解不够到位。
完整的二叉树排序代码见上一篇博客:/article/5623677.html
相关文章推荐
- Qt使用回调函数报错“函数调用缺少参数列表,请使用&xxx创建指向成员的指针”解决办法
- C语言二级指针做函数参数改变该指针的指向
- 【C++】对于指针参数在函数体内改变指向,为何从函数出来之后并没有改变?
- 改变 javascript 函数 内部 this 指针 指向 的三种方法
- JavaScript中的setTimeout和setInterval传入的函数不能带参数的解决方法
- VC6里改变对话框资源ID号后classwizard不能正常显示控件ID的原因及解决方法
- jquery ajax success 函数 异步调用方法中不能给全局变量赋值的原因及解决办法
- 指向函数的指针作为参数时的使用方法
- js 事件函数中的参数带换行符或换行标签都不能起作用的解决方法
- 以指针作为函数参数,不能改变实参指针变量的值从而改变实参值
- JS中setInterval、setTimeout不能传递带参数的函数的解决方法
- 函数调用参数改变的两种方法——指针与引用
- jquery ajax done 函数 异步调用方法中不能给全局变量赋值的原因及解决办法
- 不能从const char指针转换为LPCWSTR的原因及解决方法
- 指向指针的指针做函数的参数
- ListViewItem中的图片不能动态改变的解决方法
- error C2664: “××函数”: 不能将参数 2 从“char *”转换为“LPCWSTR” 1> 与指向的类型无关;转换要求
- C# 调用C++DLL传递指向指针的指针参数的方法
- 函数调用缺少参数列表;请使用“&****Dlg::getThread”创建指向成员的指针”--多线程函数调用
- asp.net中不能在DropDownList中选择多个项 原因分析及解决方法