值 传递
当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值:
int n = 0;
int i = n;
u = 42; //i的值改变,n的值不会变
函数传值参数的机理完全一样。函数对形参做的所有操作都不会影响到实参。
指针 传递
拷贝的是指针的值,拷贝之后,两个指针是不同的指针。
void reset(int *ip){
*ip = 0; //改变了指针ip所指对象的值
ip = 0; //只改变了ip的局部拷贝,实参未被改变
}
引用 传递
会议过去所学的知识,我们知道对于引用的操作实际上是作用在引用所引的对象上:
int n = 0, i = 42;
int &r = n; //r绑定了n(即r是n的另一个名字)
r = 42; //现在n的值是42
r = i; //现在n的值和i相同
i = r; //现在i的值和n相同
和其他引用一样,引用形参绑定了初始化它的对象。通过使用引用形参,允许函数改变一个或多个实参的值。所以可以使用引用避免拷贝,使用引用形参返回额外的信息。
void reset(int &i){
i = 0; //改了i所引对象的值
}
emmm?
形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
我抄的:
int a = 10;
int & r = a;
cout << &a << endl;
cout << &r << endl;
从上面的例子当中可以看出:它们得出的结果是相同的,这时便会有人说,r和a所占用的内存空间是相同的,因为打印出来的地址是相同的。所以这些教材上对于引用变量的操作,称之为声明一个引用变量,而非定义。因为教材的编写者认为引用变量并不占用空间。
但是,如果你使用调试器调试一下,看一下汇编的代码,就会产生新的迷惑了。
00401040 push ebp
00401041 mov ebp,esp
00401043 sub esp,48h
00401046 push ebx
00401047 push esi
00401048 push edi
00401049 lea edi,[ebp-48h]
0040104C mov ecx,12h
00401051 mov eax,0CCCCCCCCh
00401056 rep stos dword ptr [edi]
5: int a = 10;
00401058 mov dword ptr [ebp-4],0Ah
6: int & r = a;
0040105F lea eax,[ebp-4]
00401062 mov dword ptr [ebp-8],eax
从这段汇编代码来看,r和a的空间并不相同,那这又怎么解释呢?
基于此,我得出了一个非常合理的解释:
如果定义(我认为引用变量占用空间,故称之为定义)一个引用变量,这个时候引用变量实际上在内存中已经申请了一个空间,是4个字节的(32bit系统中),它本身和指针是相同的。也就是说引用和指针对于编译器本身来说操作是相同的,只是对于用户来说操作不同而已。