码迷,mamicode.com
首页 > 编程语言 > 详细

c++函数调用约定学习(二)

时间:2015-02-09 12:54:41      阅读:304      评论:0      收藏:0      [点我收藏+]

标签:

***********************************************************

首先,比较C++ 中的三种函数调用方式。

测试代码:

int _stdcall Add1(int x1, int x2, int x3)

{

     return x1 + x2 + x3;

}

 

int __cdecl Add2(int x1, int x2, int x3)

{

     return x1 + x2 + x3;

}

 

int __fastcall Add3(int x1, int x2, int x3)

{

     return x1 + x2 + x3;

}

 

void FTest2()

{

     int x[] = {1, 2, 3};

     int ret;

 

     ret = Add1(x[0], x[1], x[2]);

     ret = Add2(x[0], x[1], x[2]);

     ret = Add3(x[0], x[1], x[2]);

}

 

先看看在调用这些函数时调用者作些什么工作

__cdecl

_stdcall

__fastcall

ret = Add2(x[0], x[1], x[2]);

ret = Add1(x[0], x[1], x[2]);

ret = Add3(x[0], x[1], x[2]);

0040179F  mov         eax,dword ptr [ebp-4]

004017A2  push        eax 

004017A3  mov         ecx,dword ptr [ebp-8]

004017A6  push        ecx 

004017A7  mov         edx,dword ptr [x]

004017AA  push        edx 

004017AB  call        Add2 (401750h)

004017B0  add         esp,0Ch

004017B3  mov         dword ptr [ret],eax

0040178B  mov         eax,dword ptr [ebp-4]

0040178E  push        eax 

0040178F  mov         ecx,dword ptr [ebp-8]

00401792  push        ecx 

00401793  mov         edx,dword ptr [x]

00401796  push        edx 

00401797  call        Add1 (401760h)

 

0040179C  mov         dword ptr [ret],eax

004017B6  mov         eax,dword ptr [ebp-4]

004017B9  push        eax 

004017BA  mov         edx,dword ptr [ebp-8]

 

004017BD  mov          ecx,dword ptr [x]

 

004017C0  call        Add3 (401730h)

 

004017C5  mov         dword ptr [ret],eax

从上面可以看出调用时区别:

__cdecl 调用结束后由调用者将参数出栈

_stdcall 调用结束后不需要调用者将参数出栈

__fastcall 调用结束后不需要调用者将参数出栈,并且调用前最左侧两个参数直接存入寄存器EDX 和ECX 中

 

相同之处是三者均由右向左压栈

 

 

再看看在这些函数自己作些什么工作

__cdecl

_stdcall

__fastcall

int __cdecl Add2(int x1, int x2, int x3)

{

00401750  push        ebp 

00401751  mov         ebp,esp

    

 

 

return x1 + x2 + x3;

00401753  mov         eax,dword ptr [x1]

00401756  add         eax,dword ptr [x2]

00401759  add         eax,dword ptr [x3]

}

 

0040175C  pop         ebp 

0040175D  ret  

int _stdcall Add1(int x1, int x2, int x3)

{

00401760  push        ebp 

00401761  mov         ebp,esp

    

 

 

return x1 + x2 + x3;

00401763  mov         eax,dword ptr [x1]

00401766  add         eax,dword ptr [x2]

00401769  add         eax,dword ptr [x3]

}

 

0040176C  pop         ebp 

0040176D  ret         0Ch  

int __fastcall Add3(int x1, int x2, int x3)

{

00401730  push        ebp 

00401731  mov         ebp,esp

00401733  sub         esp,8

00401736  mov         dword ptr [ebp-8],edx

00401739  mov         dword ptr [ebp-4],ecx

     return x1 + x2 + x3;

0040173C  mov         eax,dword ptr [x1]

0040173F  add         eax,dword ptr [x2]

00401742  add         eax,dword ptr [x3]

}

00401745  mov         esp,ebp

00401747  pop         ebp 

00401748  ret          

从上面可以看出函数自身的区别:

_stdcall 函数结束后在返回时ret 命令带个参数,代表参数所占的栈大小,在返回时会把参数弹出栈

__fastcall 函数结束后ret 命令同样带个参数,并且它在函数入口把寄存器中的参数再放入栈中,fast 这个词不名副其实啊!

 

----------------------------------------------------------------------------------------------

下面,以 __cdecl 调用为例,看看函数调用前后内存栈和主要寄存器的变化

测试代码:

int __cdecl Add2(int x1, int x2, int x3)

{

     int tmp[10];

     return x1 + x2 + x3;

}

ret = Add2(x[0], x[1], x[2]);

 

1. 在ret = Add2(x[0], x[1], x[2]); 执行前

内存地址

内存中存储的值

寄存器位置

0x74H

EBP

0x68H

X[3]

 

0x64H

ret

ESP

 

2. 当执行完下面红色语句后,进入Add2 之前

     ret = Add2(x[0], x[1], x[2]);

004017AF  mov         eax,dword ptr [ebp-4]

004017B2  push         eax 

004017B3  mov         ecx,dword ptr [ebp-8]

004017B6  push        ecx 

004017B7  mov         edx,dword ptr [x]

004017BA  push        edx 

004017BB  call        Add2 (401750h)

004017C0  add         esp,0Ch

004017C3  mov         dword ptr [ret],eax

 

内存地址

内存中存储的值

寄存器位置

0x74H

EBP

0x68H

X[3]

 

0x64H

ret

 

0x60H

X3

 

0x5BH

X2

 

0x58H

X1

 

0x54H

[EIP]

ESP

可见是参数不断压入栈中,最后压入要返回时的代码地址([EIP] 的值)

 

3. 进入函数Add2 正式执行用户代码前,执行红色代码后

int __cdecl Add2(int x1, int x2, int x3)

{

00401750  push        ebp 

00401751  mov         ebp,esp

00401753  sub         esp,28h

     int tmp[10];

     return x1 + x2 + x3;

00401756  mov         eax,dword ptr [x1]

00401759  add         eax,dword ptr [x2]

0040175C  add         eax,dword ptr [x3]

}

0040175F  mov         esp,ebp

00401761  pop         ebp 

00401762  ret  

 

内存地址

内存中存储的值

寄存器位置

0x74H

 

0x68H

X[3]

 

0x64H

ret

 

0x60H

X3 (Add2())

 

0x5BH

X2

 

0x58H

X1

 

0x54H

[EIP]

 

0x50H

[EBP]

EBP

0x28H

Tmp[10]

 

ESP

这里主要工作是保存EBP, 并设置到栈顶,这样可以通过EBP 访问该函数中的局部变量。另外,由于函数内声明了局部变量 int tmp[10] ,占用大小 10 * 4 = 28H 个字节,所以把ESP 下移。(栈总是向下扩展)

 

4. 然后是执行函数体代码,返回值放入EAX( 或AX,AL) 中。最后返回,返回前重设ESP,EBP 的值,通过栈中的[EIP] 的值返回到调用函数之前的代码处。

最终:

内存地址

内存中存储的值

寄存器位置

0x74H

EBP

0x68H

X[3]

 

0x64H

ret

ESP

0x60H

X3 ( 没用了)

 

0x5BH

X2 ( 没用了)

 

0x58H

X1 ( 没用了)

 

0x54H

[EIP] ( 没用了)

 

0x50H

[EBP] ( 没用了)

 

0x28H

Tmp[10] ( 没用了)

 

 

 

c++函数调用约定学习(二)

标签:

原文地址:http://blog.csdn.net/ghevinn/article/details/43668701

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!