`
foreversunyao
  • 浏览: 203998 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

传值与传地址的区别---转载

阅读更多


2008-11-16 10:39
传值, 
是把实参的值赋值给行参 
那么对行参的修改,不会影响实参的值 

传地址 
是传值的一种特殊方式,只是他传递的是地址,不是普通的如int 
那么传地址以后,实参和行参都指向同一个对象 

传引用 
真正的以地址的方式传递参数 
传递以后,行参和实参都是同一个对象,只是他们名字不同而已 
对行参的修改将影响实参的值
-----------------------------------------------------------------------------------
觉得从函数调用的角度理解比较好 

传值: 
函数参数压栈的是参数的副本。 
任何的修改是在副本上作用,没有作用在原来的变量上。 

传指针: 
压栈的是指针变量的副本。 
当你对指针解指针操作时,其值是指向原来的那个变量,所以对原来变量操作。 

传引用: 
压栈的是引用的副本。由于引用是指向某个变量的,对引用的操作其实就是对他指向的变量的操作。(作用和传指针一样,只是引用少了解指针的草纸)
-----------------------------------------------------------------------------------
函数参数传递机制的基本理论 
  函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题。基本的参数传递机制有两种:值传递和引用传递。以下讨论称调用其他函数的函数为主调函数,被调用的函数为被调函数。 
  值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。 
  引用传递(pass-by-reference)过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的 
实参变量。  
-----------------------------------------------------------------------------------

仅讨论一下值传递和引用: 
所谓值传递,就是说仅将对象的值传递给目标对象,就相当于copy;系统将为目标对象重新开辟一个完全相同的内存空间。 
所谓引用,就是说将对象在内存中的地址传递给目标对象,就相当于使目标对象和原始对象对应同一个内存存储空间。此时,如果对目标对象进行修改,内存中的数据也会改变。

参数传值与传址
2008年11月11日 星期二 10:52 A.M.

参数是调用函数的代码,传给函数的数据,在C,C++中,参数有两种传递方式:传值方式(它是程序中最常见的传递参数的方法)和传址方式(函数对参数的操作,将直接改变实参的值)。这两个名词分别指:传递“参数的值”和传递“参数的地址”。

“参数的传递方式”,“参数的传递过程”,方式和过程有何区别?中学时我对前桌的女生“有意思”,想给人家传递点信息,是往她家打个电话呢?还是来个“小纸条”?这就是“传递方式”的不同。我选择了后者。至于传递过程:刚开始时我把纸条裹在她的头发里,下课时假装关心地“喂,你的头发里掉了张纸……”。后来大家熟了,上课时我轻轻动一下她的后背,她就会不自在,然后在一个合适时机,自动把手别过来取走桌沿的纸条……这就是传递过程的不同吧?(以上故事纯属虚构)

程序是在内存里运行的。所以无论参数以哪一种方式传递,都是在内存中“传来传去”。在一个程序运行时,程序会专门为参数开辟一个内存空间,称为“栈”。栈所在内存空间位于整个程序所占内存的顶部(为了直观,我们将地址较小的内存画在示意图顶部,如果依照内存地址由下而上递增规则,则栈区应该在底部),如图:

当程序需要传递参数时,将一个个参数“压入”栈区内存的底部,然后,函数再从栈区一个个读出参数。

如果一个函数需要返回值,那么调用者首先需要在栈区留出一个大小正好可以存储返回值的内存空间,然后再执行参数的入栈操作。

假设有一函数:int AddTwoNum(int n1, int n2)     然后在代码某处调用:

....

int a = 1;

int b = 2;

int c = AddTwoNum(a,b);

当执行上面黑体部分,即调用函数的动作发生时,栈区出现下面的操作:

图中标明为返回值预留的空间大小是4个字节,当然不是每个函数都这个大小。它由函数返回值的数据类型决定,本函数AddTwoNum返回值是int类型,所以为4个字节。其它的a,b参数也是int类型,所以同样各占4字节大小的内存空间。

至于参数是a还是b先入栈,这依编译器而定,大都数编译器采用“从右到左的次序”将参数一个个压入。所以本示意图,参数b被先“压”入在底部,然后才是a。这样就完成了参数的入栈过程。根据前面讲的不同“传递方式”,被实际压入栈的数据也就不同。

一、如果是“传值”,则栈中的a,b就是“复制品”,对二者的操作,仅仅是改变此处栈区的内存,和调用处的实参:a,b毫不关联:

二、而在“传址”方式时,编译器会将调用处的a,b的内存地址写入栈区,并且将函数中所有对该栈区内存的操作,都转向调用处a,b的内存地址。请看:看起来二的图比一要复杂得多。其实实质的区别并不多。

在一图中,传给函数的是a,b的值,即1,2;

在二图中,传给函数的是a,b的地址,即:00129980,00129984。

“参数的传递过程”说到最后,还是和“参数的传递方式”纠缠在一起。我个人认为,在刚开始学习C++时,并不需要--或者甚至就是最好不要--去太纠缠语言内部实现的机制,而重在于运用。下面我们就来举一个使用“传址”方式的例子。

题目是:写一函数,实现将两个整型变量的值互换。

幸好实现它也非常的简单和直观。典型的方法是使用“第三者”你可能感到不解:交换两个变量的值,就让这两个变量自个互换就得了,比如小明有个苹果,小光有个梨子,两人你给我给你就好了啊,要小兵来做什么?

呵,你看吧:

int a = 1, b = 2;

//不要“第三者”的交换(失败)

a = b;

b = a;

好好看看,好好想想吧。当执行交换的第一句:a = b;时,看去工作得不错,a的值确实由1变成了2。然后再下去呢?等轮到b想要得到a的值时,a现在和b其实相等,都是2,结果b=a;后,b的值还是2.没变。

只好让“第三者”插足了……反正程序没有婚姻法。

int a = 1, b =2;

int c ; //“第三者”

//交换开始:

c = a;

a = b;

b = c;

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics