一案例代码(VS2010C++环境下调试)
#include"stdafx.h"
int&__stdcallRefFun(int&n)//通过引用传递参数
{
n++;
returnn;
}
int__stdcallValueFun(intn)//通过变量值传递参数
{
n++;
returnn;
}
intmain(intargc,char*argv[])
{
inta=10;
int&x=a;//引用变量初始化
int*pInt=&a;//取普通变量的地址
int*pref=&x;//取引用变量的地址(其实获取的并不是引用变量的地址,而是被引用变量a的地址)
int&b=RefFun(a);//函数返回引用,并将引用的别名赋值给引用变量
intc=RefFun(a);//函数返回引用,并将引用变量的值赋值给整数变量
intd=ValueFun(a);//函数返回变量值,
printf("a=%d,b=%d,c=%d\n",a,b,c);
return0;
}
二对上面的Main函数反汇编代码分析
注:
1)以下是在VS2010下,使用C++工程进行调试(切换到反汇编模式)显示的代码;
2)每行源代码下面为其对应的汇编代码;
3)读者也可以在自己的VS2010开发平台下进行反汇编分析!
反汇编分析代码如下:
intmain(intargc,char*argv[])
{
004295E0pushebp
004295E1movebp,esp
004295E3subesp,0F0h
004295E9pushebx
004295EApushesi
004295EBpushedi
004295ECleaedi,[ebp-0F0h]
004295F2movecx,3Ch
004295F7moveax,0CCCCCCCCh
004295FCrepstosdwordptre
inta=10;
//
004295FEmovdwordptr[a],0Ah
int&x=a;//引用变量初始化:其实质就是保存变量a的地址值到引用变量的内存单元!
004272B5leaeax,[a]
004272B8movdwordptr[x],eax
int*pInt=&a;//取普通变量的地址
004272BBleaeax,[a]//[a]表示变量a的地址;
004272BEmovdwordptr[pInt],eax//变量a的地址保存到指针变量pInt中:这也是指针变量的原理;
//下面的代码取引用变量的地址值,在实际很少用到该方式,在这里仅用于案例说明!
int*pref=&x;//取引用变量地址
004272C1moveax,dwordptr[x]//取引用变量内存单元保存的值(不是引用变量地址)
004272C4movdwordptr[pref],eax//引用变量内存单元值保存到指针变量;
//对上面两行源代码的汇编分析补充:
//1)[x]表示引用变量x内存单元地址,dwordptr[x]:表示内存单元X保存的值(实际是一个地址值,实际指向被引用的变量a的值);
//2)引用变量的实现秘密:引用变量在内部实现其实就是一个常量指针变量;
//3)分析上面取普通变量地址,与取引用的地址内部实现机理是不一样的;
int&b=RefFun(a);//函数返回引用,并将引用的别名赋值给引用变量
//对应的汇编代码:
00429605leaeax,[a]//取变量a地址到EAX
00429608pusheax//变量a的地址为:0x4260FEh
00429609callRefFun(4260FEh)//引用传递变量地址(指针)--引用实现的内部秘密!
0042960Emovdwordptr[b],eax//EAX返回的为变量a的地址:实际为变量a的地址(初始化引用变量b)
intc=RefFun(a);//函数返回引用,并将引用变量的值赋值给整数变量
//上面代码对应的汇编代码:
00429611leaeax,[a]
00429614pusheax//变量a的地址入栈(传递给函数的引用参数实际是变量的地址)
00429615callRefFun(4260FEh)
0042961Amovecx,dwordptr[eax]
0042961Cmovdwordptr[c],ecx
intd=ValueFun(a);
//上面代码对应的汇编代码:
0042961Fmoveax,dwordptr[a]//取变量a的值送到EAX寄存器
00429622pusheax//变量a的值入栈(传递给函数:ValueFun);
00429623callValueFun(426103h)
00429628movdwordptr[d],eax
printf("a=%d,b=%d,c=%d\n",a,b,c);
0042962Bmoveax,dwordptr[c]
0042962Epusheax
0042962Fmovecx,dwordptr[b]
00429632movedx,dwordptr[ecx]
00429634pushedx
00429635moveax,dwordptr[a]
00429638pusheax
00429639pushoffsetstring
0042963Ecall@ILT+3875(_printf)(425F28h)
00429643addesp,10h
return0;
00429646xoreax,eax
}
1对上面案例代码引用传递参数的RefFun分析--反汇编分析
int&__stdcallRefFun(int&n)//引用传递参数
{
00429560pushebp
00429561movebp,esp
00429563subesp,0C0h
00429569pushebx
0042956Apushesi
0042956Bpushedi
0042956Cleaedi,[ebp-0C0h]
00429572movecx,30h
00429577moveax,0CCCCCCCCh
0042957Crepstosdwordptre
n++;//
//下面为对应的汇编代码,注意与下面函数ValueFun通过变量值传递参数的方式区别:
0042957Emoveax,dwordptr[n]//eax保存传递过来引用变量的地址值
00429581movecx,dwordptr[eax]//取到传递过来的变量值
00429583addecx,1
00429586movedx,dwordptr[n]//引用变量的地址保存到EDX
00429589movdwordptr[edx],ecx//保存加1后的结果;
returnn;//由于变量n保存的为变量的地址,因此这里返回的是传递进来的地址值(区别于传值)
0042958Bmoveax,dwordptr[n]
}
2对上面案例代码引用传递参数的ValueFun分析--反汇编分析
int__stdcallValueFun(intn)//变量值传递参数
{
004295A0pushebp
004295A1movebp,esp
004295A3subesp,0C0h
004295A9pushebx
004295AApushesi
004295ABpushedi
004295ACleaedi,[ebp-0C0h]
004295B2movecx,30h
004295B7moveax,0CCCCCCCCh
004295BCrepstosdwordptre
n++;//
004295BEmoveax,dwordptr[n]//dwordptr[n]为栈上的临时变量n的地址;
004295C1addeax,1
004295C4movdwordptr[n],eax
returnn;
//对应的汇编代码:返回的是栈上变量的值(这一点区别于引用,引用返回地址)
004295C7moveax,dwordptr[n]
}
四引用的实现秘密
通过对上面二,三章代码的反汇编代码的实现分析,对于引用变量的内部实现,可以得出如下结论:
1引用变量:
1)引用的内部实现为相当于一个指针变量,与指针的实现方式类似;
2)引用变量内存单元保存的指向变量地址(初始化时赋值),与指针不同地方时,引用变量在定义时必须初始化(猜测是为避免语法歧义),而且使用过程中,引用变量保存的内存单元地址值是不能改变的(这一点通过编译器来实现保证);
3)引用也可以进行取地址操作,但是取地址操作返回的不是引用变量所在的内存单元地址,而是被引用变量本身所在的内存单元地址;(对引用变量的取地址操作相当于取内容操作,如果要想取得引用变量的地址,应使用两次取地址符号,如:&(&a))
4)引用的使用,在源代码级相当于普通的变量一样使用,但在函数参数传递引用变量时,内部传递的实际是变量的地址值(这种机制的实现是通过编译器(编译手段)来实现的)。
具体还可以参考:《c++语言引用变量实现机制浅析》,刘维富,南通工学院学报
五关于汇编语言的补充
1学习掌握汇编语言的原理(注:这里是原理学习,不是学习使用汇编语言进行代码编写),对提高软件开发者程序分析,提高对计算机系统实现原理的理解(如堆,栈,函数运行分析,以及掌握操作系统的运行实现机制,包括栈的生长方向等理解),都是非常有帮助;
2通过汇编分析,所有的实现机理都可以通过调试分析出来,对提高开发者的分析能力是非常有帮助的!包括对于C++的很多知识点,都可以通过反汇编分析,可以真正了解和掌握其背后的运行机理,提高对语言的正确运用;
联系客服