您的位置:首页 > 编程语言 > C语言/C++

关于C++引用的一个探索

2008-08-22 14:24 183 查看
一直以来对”引用”这个东西都有疑惑,今天决心要把它弄清楚.
环境:VC6.0 + XP SP2

实验一:
用VC6.0编译如下代码:
void c(int &b)
{
b+=2;
}
void main()
{
int a=5;
cout<<a<<endl;
int &b=a;
cin>>b;
c(b);
cin>>b;
}
然后将生成的程序反汇编,得到如下代码:
00401010 /$ 51 PUSH ECX 00401011 |. 6A 05 PUSH 5 /Arg1 = 00000005
00401013 |. B9 40CA4000 MOV ECX,cri.0040CA40 ; |
00401018 |. C74424 04 050>MOV DWORD PTR SS:[ESP+4],5 ;这里就是int a=5;此时ESP=0013FF7C
00401020 |. E8 2D020000 CALL cri.00401252 ; /cri.00401252
00401025 |. 68 90104000 PUSH cri.00401090
0040102A |. 6A 0A PUSH 0A ; /Arg1 = 0000000A
0040102C |. 8BC8 MOV ECX,EAX ; |
0040102E |. E8 81010000 CALL cri.004011B4 ; /cri.004011B4
00401033 |. 8BC8 MOV ECX,EAX
00401035 |. E8 36000000 CALL cri.00401070 ;这里就是cout<<a<<endl;
0040103A |. 8D4424 00 LEA EAX,DWORD PTR SS:[ESP] ;ESP=0013FF80,这里意思是把a的地址放到寄存器EAX中
0040103E |. B9 F0C94000 MOV ECX,cri.0040C9F0
00401043 |. 50 PUSH EAX ;把a的地址压栈,或者说把指向a的指针压栈,这时堆栈中0013FF7C保存的就是指向a的指针(0013FF80)
00401044 |. E8 57000000 CALL cri.004010A0 ;跟进这里

接着看到如下代码:
004010A0 /$ 55 PUSH EBP
004010A1 |. 8BEC MOV EBP,ESP
004010A3 |. 83EC 10 SUB ESP,10
004010A6 |. 56 PUSH ESI
004010A7 |. 8BF1 MOV ESI,ECX
004010A9 |. 6A 00 PUSH 0
004010AB |. E8 06090000 CALL cri.004019B6 ;这里就是第一个cin>>b;这里我输入”90”,程序以ASCII码保存在0013FF64=00003039
004010B0 |. 85C0 TEST EAX,EAX
004010B2 |. 74 46 JE SHORT cri.004010FA
004010B4 |. 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
004010B7 |. 8BCE MOV ECX,ESI
004010B9 |. 50 PUSH EAX ; /Arg1
004010BA |. E8 95060000 CALL cri.00401754 ; /cri.00401754
004010BF |. 50 PUSH EAX
004010C0 |. 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
004010C3 |. 6A 00 PUSH 0
004010C5 |. 50 PUSH EAX ;把字符串”90”压栈
004010C6 |. E8 3A120000 CALL cri.00402305 ;这里就是把”90”这个字符串转成整数90,相当于函数atoi()的功能,这个call过之后EAX就等于十六进制5A
004010CB |. B9 FFFFFF7F MOV ECX,7FFFFFFF
004010D0 |. 83C4 0C ADD ESP,0C
004010D3 |. 3BC1 CMP EAX,ECX
004010D5 |. 7E 15 JLE SHORT cri.004010EC
004010D7 |> 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
004010DA |. 8908 MOV DWORD PTR DS:[EAX],ECX
004010DC |. 8B06 MOV EAX,DWORD PTR DS:[ESI]
004010DE |. 8B40 04 MOV EAX,DWORD PTR DS:[EAX+4]
004010E1 |. 834C30 08 02 OR DWORD PTR DS:[EAX+ESI+8],2
004010E6 |. 8D4430 08 LEA EAX,DWORD PTR DS:[EAX+ESI+8]
004010EA |. EB 0E JMP SHORT cri.004010FA
004010EC |> B9 00000080 MOV ECX,80000000
004010F1 |. 3BC1 CMP EAX,ECX
004010F3 |.^ 7C E2 JL SHORT cri.004010D7
004010F5 |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8] ;此时EBP+8=0013FF7C,这里保存的就是指向a的指针,意思就是把0013FF7C内存单元中的值(0013FF80)放到ECX中
004010F8 |. 8901 MOV DWORD PTR DS:[ECX],EAX ;把十进制数90放到地址为0013FF80的内存单元中,从这里可以看出cin>>b;本质上就是cin>>a;
004010FA |> 8BC6 MOV EAX,ESI
004010FC |. 5E POP ESI
004010FD |. C9 LEAVE
004010FE /. C2 0400 RETN 4 ;函数返回

出来后就到这里:
00401049 |. 8D4C24 00 LEA ECX,DWORD PTR SS:[ESP]
0040104D |. 51 PUSH ECX ;这里ECX=0013FF80,也就是调用c(int &b)函数时传了个指向a的指针进去
0040104E |. E8 ADFFFFFF CALL cri.00401000 ;这里就是c(b);跟进去

看到如下代码:
00401000 /$ 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] ;此时ESR+4=0013FF7C,意思是把0013FF80放到EAX中,也就是把a的值放到EAX
00401004 |. 8300 02 ADD DWORD PTR DS:[EAX],2 ;这里就是b+=2;
00401007 /. C3 RETN
通过上面代码可以发现,传进的参数b根本没用到,而且这个时候b被解析成了指向a的指针

00401053 |. 83C4 04 ADD ESP,4
00401056 |. 8D5424 00 LEA EDX,DWORD PTR SS:[ESP]
0040105A |. B9 F0C94000 MOV ECX,cri.0040C9F0
0040105F |. 52 PUSH EDX ;这里EDX=0013FF80
00401060 |. E8 3B000000 CALL cri.004010A0 ;这里就是第二个cin>>b;其实看压栈的参数可以知道这里的b直接就被编译解析成a了,等价于cin>>a;
00401065 |. 59 POP ECX
00401066 /. C3 RETN ;出去程序就结束了

实验二:
接着把代码改一下,改后如下:
void main()
{
int a=5;
cout<<a<<endl;
int &b=a;
cin>>b;
cin>>b;
}
没有函数定义和调用了,再次反汇编,得到的只是比上面少了函数调用和定义,其它都一样,而且在0013FF7C这个内存单元还是保存着0013FF80,而0013FF80内存单元保存着a的值

实验三:
在改一下上面刚改过的代码,改成如下:
void main()
{
int a=5;
cout<<a<<endl;
int &b=a;
int &c=a;
cin>>b;
cin>>b;
}
再次编译,结果和第二次编译的完全一样

通过上面的实验,可以得到这样的结论:
同一个函数里面的引用在用的时候都是直接用被引用的数,它的”别名”根本没用到,但是编译器还是为”别名”分配了内存,而且”别名”都被编译成指针了,当函数的参数是引用某个数的时候,其实传进去的就是一个指向被引用的变量的指针.
例如void func(int &b);就最终编译的时候变成void func(int *b);

还有,实验三证明了定义多个引用(我这里三个,四个或更多的引用没帖上来,结果都是一样的)编译器都只会分配一个内存给这些别名.

(注:我这里讲”编译器为***分配内存”是迫于不知道怎么表达,估计大家会理解我也能理解我.)

我个人感觉引用其实只是很概念的东西,虽说是别名,同一个变量的引用还是同一个指向它的指针,和多个指针一样,只是在概念上和编写代码上给我们提供和指针不同的方式,有时也会带来一些方便,或许也是从概念上改变指针(毕竟指针被认为是万恶之源),让大家编写代码的时候不为指针的边界操心,就像JAVA和C#说的一样,”没有指针了”,其实这句话是说”大家不用担心指针带来的边界问题了,这个由编译器自行解决”.以上属于个人理解,我还小,估计这样的理解有很多错误,错误之处请大家纠正.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: