返回值是一个struct或class对象的情形【4】

C++代码:

struct AB
{
	int ia;
	int ib;
	int ic;
	AB(){}

};
AB su_4()    // 返回一个对象
{
	AB c;
	c.ia=100;
	c.ib=150;
	c.ic=200;
	
	return c;
}


int main()
{
	AB r;
	r=su_4();  // 函数调用

	getchar();
	return 0;
}



        F7编译连接(debug模式)。加断点,按F5调试,按alt+8出现反汇编窗口。汇编代码如下:


    51: int main()
    52: {
004115F0 55                   push        ebp  				% 【1】这一段为main函数干正事之前的准备工作
004115F1 8B EC                mov         ebp,esp  
004115F3 81 EC E8 00 00 00    sub         esp,0E8h  
004115F9 53                   push        ebx  
004115FA 56                   push        esi  
004115FB 57                   push        edi  
004115FC 8D BD 18 FF FF FF    lea         edi,[ebp-0E8h]  
00411602 B9 3A 00 00 00       mov         ecx,3Ah  
00411607 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
0041160C F3 AB                rep stos    dword ptr es:[edi]  		% end 【1】
    60: 	AB r;
0041160E 8D 4D F0             lea         ecx,[r]  			% 【2】声明对象,调用构造函数
00411611 E8 30 FA FF FF       call        AB::AB (411046h)  
    61: 	r=su_4();
00411616 8D 85 1C FF FF FF    lea         eax,[ebp-0E4h]   % 【3】调用su_4()函数。该函数没有输入参数,但由于返回值为对象(体积大于64bit),因此要用地址来                                                            %  传递。这里将地址设为ebp-0E4h,是一个栈地址,但离当前栈顶很远。此时将地址值存入eax,再push之
0041161C 50                   push        eax  
0041161D E8 65 FA FF FF       call        su_4 (411087h)   % 调用函数。eip自动入栈。
00411622 83 C4 04             add         esp,4  	   % 由于之前push了eax,现在将esp加回去,实现栈平衡。
00411625 8B 08                mov         ecx,dword ptr [eax]  		% eax保存了函数返回值临时存储区首地址。总共12字节,这里分三次mov完成拷贝。将临时存                                                                        % 储区里的数据拷贝到接收变量r中去。
00411627 89 4D F0             mov         dword ptr [r],ecx  
0041162A 8B 50 04             mov         edx,dword ptr [eax+4]  
0041162D 89 55 F4             mov         dword ptr [ebp-0Ch],edx  
00411630 8B 40 08             mov         eax,dword ptr [eax+8]  
00411633 89 45 F8             mov         dword ptr [ebp-8],eax  

 
   105: 	getchar();					% 【4】剩下的是main函数的后处理,不用管。
00411636 8B F4                mov         esi,esp  
00411638 FF 15 B4 82 41 00    call        dword ptr [__imp__getchar (4182B4h)]  
0041163E 3B F4                cmp         esi,esp  
00411640 E8 FB FA FF FF       call        @ILT+315(__RTC_CheckEsp) (411140h)  
   106: 	return 0;
00411645 33 C0                xor         eax,eax  
   107: }
00411647 52                   push        edx  
00411648 8B CD                mov         ecx,ebp  
0041164A 50                   push        eax  
0041164B 8D 15 6C 16 41 00    lea         edx,[ (41166Ch)]  
00411651 E8 2C FA FF FF       call        @ILT+125(@_RTC_CheckStackVars@8) (411082h)  
00411656 58                   pop         eax  
00411657 5A                   pop         edx  
00411658 5F                   pop         edi  
00411659 5E                   pop         esi  
0041165A 5B                   pop         ebx  
0041165B 81 C4 E8 00 00 00    add         esp,0E8h  
00411661 3B EC                cmp         ebp,esp  
00411663 E8 D8 FA FF FF       call        @ILT+315(__RTC_CheckEsp) (411140h)  
00411668 8B E5                mov         esp,ebp  
0041166A 5D                   pop         ebp  
0041166B C3                   ret  


构造函数:
   26: struct AB
    27: {
    28: 	int ia;
    29: 	int ib;
    30: 	int ic;
    31: 	AB(){}
004115B0 55                   push        ebp  
004115B1 8B EC                mov         ebp,esp  
004115B3 81 EC CC 00 00 00    sub         esp,0CCh  
004115B9 53                   push        ebx  
004115BA 56                   push        esi  
004115BB 57                   push        edi  
004115BC 51                   push        ecx  
004115BD 8D BD 34 FF FF FF    lea         edi,[ebp-0CCh]  
004115C3 B9 33 00 00 00       mov         ecx,33h  
004115C8 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
004115CD F3 AB                rep stos    dword ptr es:[edi]  
004115CF 59                   pop         ecx  
004115D0 89 4D F8             mov         dword ptr [ebp-8],ecx  
004115D3 8B 45 F8             mov         eax,dword ptr [this]  
004115D6 5F                   pop         edi  
004115D7 5E                   pop         esi  
004115D8 5B                   pop         ebx  
004115D9 8B E5                mov         esp,ebp  
004115DB 5D                   pop         ebp  
004115DC C3                   ret  
};



函数:
   35: AB su_4()
    36: {
004114F0 55                   push        ebp  		%【1】 函数预处理工作。
004114F1 8B EC                mov         ebp,esp  
004114F3 81 EC D4 00 00 00    sub         esp,0D4h  
004114F9 53                   push        ebx  
004114FA 56                   push        esi  
004114FB 57                   push        edi  
004114FC 8D BD 2C FF FF FF    lea         edi,[ebp-0D4h]  
00411502 B9 35 00 00 00       mov         ecx,35h  
00411507 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
0041150C F3 AB                rep stos    dword ptr es:[edi]  
    37: 	AB c;
0041150E 8D 4D F0             lea         ecx,[c]  	%【2】 定义临时变量,调用构造函数。
00411511 E8 30 FB FF FF       call        AB::AB (411046h)  
    38: 	c.ia=100;
00411516 C7 45 F0 64 00 00 00 mov         dword ptr [c],64h  	% 【3】 给三个成员变量赋值。
    39: 	c.ib=150;
0041151D C7 45 F4 96 00 00 00 mov         dword ptr [ebp-0Ch],96h  
    40: 	c.ic=200;
00411524 C7 45 F8 C8 00 00 00 mov         dword ptr [ebp-8],0C8h  
    41: 	
    42: 	return c;
0041152B 8B 45 08             mov         eax,dword ptr [ebp+8]   %【4】 重点来了。由ebp指向入栈的上级函数ebp,ebp+4指向入栈的eip,eip+8指向入栈的临时返回                                                                  % 值地址。这里将该地址值存入eax。
0041152E 8B 4D F0             mov         ecx,dword ptr [c]       % [c]其实是[ebp-4]。将临时变量c的值拷贝到临时返回值存储区。
00411531 89 08                mov         dword ptr [eax],ecx  
00411533 8B 55 F4             mov         edx,dword ptr [ebp-0Ch]  
00411536 89 50 04             mov         dword ptr [eax+4],edx  
00411539 8B 4D F8             mov         ecx,dword ptr [ebp-8]  
0041153C 89 48 08             mov         dword ptr [eax+8],ecx  
0041153F 8B 45 08             mov         eax,dword ptr [ebp+8]  
    43: }
00411542 52                   push        edx  			 % 【5】 一些后处理。
00411543 8B CD                mov         ecx,ebp  
00411545 50                   push        eax  
00411546 8D 15 68 15 41 00    lea         edx,[ (411568h)]  
0041154C E8 31 FB FF FF       call        @ILT+125(@_RTC_CheckStackVars@8) (411082h)  
00411551 58                   pop         eax  
00411552 5A                   pop         edx  
00411553 5F                   pop         edi  
00411554 5E                   pop         esi  
00411555 5B                   pop         ebx  
00411556 81 C4 D4 00 00 00    add         esp,0D4h  
0041155C 3B EC                cmp         ebp,esp  
0041155E E8 DD FB FF FF       call        @ILT+315(__RTC_CheckEsp) (411140h)  
00411563 8B E5                mov         esp,ebp  
00411565 5D                   pop         ebp  
00411566 C3                   ret 

--------------------------------------------------------------------------------------------分割线--------------------------------------------------------------------------------------------

以上为有构造函数的情形,struct解释为一个类。如果没有构造函数,就会多出一次拷贝:

   51: int main()
    52: {
004115A0 55                   push        ebp  
004115A1 8B EC                mov         ebp,esp  
004115A3 81 EC 00 01 00 00    sub         esp,100h  
004115A9 53                   push        ebx  
004115AA 56                   push        esi  
004115AB 57                   push        edi  
004115AC 8D BD 00 FF FF FF    lea         edi,[ebp-100h]  
004115B2 B9 40 00 00 00       mov         ecx,40h  
004115B7 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
004115BC F3 AB                rep stos    dword ptr es:[edi]  
004115BE A1 00 70 41 00       mov         eax,dword ptr [___security_cookie (417000h)]  
004115C3 33 C5                xor         eax,ebp  
004115C5 89 45 FC             mov         dword ptr [ebp-4],eax  

    60: 	AB r;
    61: 	r=su_4();
004115C8 8D 85 18 FF FF FF    lea         eax,[ebp-0E8h]  
004115CE 50                   push        eax  
004115CF E8 AE FA FF FF       call        su_4 (411082h)  
004115D4 83 C4 04             add         esp,4  
004115D7 8B 08                mov         ecx,dword ptr [eax]  			% 两次拷贝
004115D9 89 8D 04 FF FF FF    mov         dword ptr [ebp-0FCh],ecx  
004115DF 8B 50 04             mov         edx,dword ptr [eax+4]  
004115E2 89 95 08 FF FF FF    mov         dword ptr [ebp-0F8h],edx  
004115E8 8B 40 08             mov         eax,dword ptr [eax+8]  
004115EB 89 85 0C FF FF FF    mov         dword ptr [ebp-0F4h],eax  
004115F1 8B 8D 04 FF FF FF    mov         ecx,dword ptr [ebp-0FCh]  
004115F7 89 4D EC             mov         dword ptr [ebp-14h],ecx  
004115FA 8B 95 08 FF FF FF    mov         edx,dword ptr [ebp-0F8h]  
00411600 89 55 F0             mov         dword ptr [ebp-10h],edx  
00411603 8B 85 0C FF FF FF    mov         eax,dword ptr [ebp-0F4h]  
00411609 89 45 F4             mov         dword ptr [ebp-0Ch],eax  
    62: 
   104: 
   105: 	getchar();
0041160C 8B F4                mov         esi,esp  
0041160E FF 15 B4 82 41 00    call        dword ptr [__imp__getchar (4182B4h)]  
00411614 3B F4                cmp         esi,esp  
00411616 E8 20 FB FF FF       call        @ILT+310(__RTC_CheckEsp) (41113Bh)  
   106: 	return 0;
0041161B 33 C0                xor         eax,eax  
   107: }
0041161D 52                   push        edx  
0041161E 8B CD                mov         ecx,ebp  
00411620 50                   push        eax  
00411621 8D 15 4C 16 41 00    lea         edx,[ (41164Ch)]  
00411627 E8 51 FA FF FF       call        @ILT+120(@_RTC_CheckStackVars@8) (41107Dh)  
0041162C 58                   pop         eax  
0041162D 5A                   pop         edx  
0041162E 5F                   pop         edi  
0041162F 5E                   pop         esi  
00411630 5B                   pop         ebx  
00411631 8B 4D FC             mov         ecx,dword ptr [ebp-4]  
00411634 33 CD                xor         ecx,ebp  
00411636 E8 D9 F9 FF FF       call        @ILT+15(@__security_check_cookie@4) (411014h)  
0041163B 81 C4 00 01 00 00    add         esp,100h  
00411641 3B EC                cmp         ebp,esp  
00411643 E8 F3 FA FF FF       call        @ILT+310(__RTC_CheckEsp) (41113Bh)  
00411648 8B E5                mov         esp,ebp  
0041164A 5D                   pop         ebp  
0041164B C3                   ret  

基本上有3次拷贝:在子函数中,将变量拷贝到临时返回值存储区,在调用端(caller),将其拷贝到另一个临时存储区,再从那个存储区中拷贝到接受的变量。




明确下函数调用的过程:

(1)输入参数从右到左push进堆栈,如果是引用的话就push变量的地址;

(2)如果返回值是很大的对象(大于64bit),而且是直接返回,不是返回引用(返回引用的话就直接返回地址),那么找一块很远的内存,将其地址push入栈,将来用来临时存放返回值

(3)指向call func。func为函数名称。此时eip寄存器的内容自动入栈。

(4)从这一步开始就进入函数了!Push ebp,将caller的ebp入栈。

(5)mov ebp, esp。当前栈顶作为栈基址。callee有了自己的栈空间!

(6)sub esp,100.100是随便写的,可能是别的数。系统分配临时变量的空间。

(7)ebx,esi,edi等寄存器入栈。

(8)用0xCC填充以上申请的空间。

(9)干正事。。。

(10)函数要返回了。返回值是一个大的对象。将对象拷贝到ebp+8那个地方的值所指的位置。没错,就是指的第(2)步那么临时存放返回值的地方。

(11)mov esp,ebp。清理栈空间其实很简单,改变栈顶位置即可。

(12)ret。这条命令弹出栈中eip对应的值,这样系统才知道调用这个函数以后下一条指令是什么。

(13)add esp,XXX。因为有输入参数,还有临时存放空间指针,所以这里要做栈平衡。

(14)从刚才说的返回值临时存放空间将返回值拷到另一个临时存放空间;如果有变量接受这个返回值,则从新的临时存放空间再将其拷到该变量上了如果没有变量接受,那么就不用了。

(15)调用结束,该干啥干啥。

注意:栈空间一般默认1M,递归过深会导致栈溢出。



ref:

http://wenku.baidu.com/view/bea115ccda38376baf1faec7.html

http://www.cnblogs.com/zplutor/archive/2011/09/25/2190315.html



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值