现在的位置: 首页 > 综合 > 正文

call conversion 函数调用约定

2017年11月05日 ⁄ 综合 ⁄ 共 4977字 ⁄ 字号 评论关闭
 

函数调用约定

分类: c/c++ 253人阅读 评论(0) 收藏 举报

函数调用约定就是描述参数如何传递,堆栈由调用方还是被调用方平衡,返回值如何返回等规则。

函数调用约定的几种类型有:__stdcall, __cdecl, __fastcall, __thiscall, __nakedcall, __pascal

下面介绍几种常见的函数调用约定(以VS2010编译器为例):

(1) __cdecl调用约定

1. 参数从右向左传递,放在栈中

2. 栈平衡由调用函数来执行

3. 不定参数的函数可以使用

下面看一个汇编的例子

  1. int a = 1, b = 2;  
  2. mov         dword ptr [a],1    
  3. mov         dword ptr [b],2    
  4. int sum = Sum(a, b);  
  5. mov         eax,dword ptr [b]    // 参数从右向左压入栈,压入参数b  
  6. push        eax    
  7. mov         ecx,dword ptr [a]    // 参数从右向左压入栈,压入参数a  
  8. push        ecx    
  9. call        Sum (13611A9h)       // 调用函数  
  10. add         esp,8                // 调用方平衡堆栈(弹出参数)  
  11. mov         dword ptr [sum],eax  // 返回值保存在eax中  
  1. int __cdecl Sum(int a, int b)  
  2. {  
  3.  push        ebp                 // 保存上一层函数栈底指针  
  4.  mov         ebp,esp             // 设置本层函数栈底指针  
  5.  sub         esp,0C0h            // 设置本层函数栈顶指针  
  6.  push        ebx                 // 保存寄存器的值:ebx、esi、edi  
  7.  push        esi    
  8.  push        edi    
  9.  lea         edi,[ebp-0C0h]      // 栈内容赋初值(调试代码中使用)  
  10.  mov         ecx,30h    
  11.  mov         eax,0CCCCCCCCh    
  12.  rep stos    dword ptr es:[edi]    
  13.     return a+b;  
  14.  mov         eax,dword ptr [a]    
  15.  add         eax,dword ptr [b]  // 将返回值保存在eax中  
  16. }  
  17.  pop         edi                // 恢复寄存器的值:edi、esi、ebx(相反的顺序弹出)  
  18.  pop         esi    
  19.  pop         ebx    
  20.  mov         esp,ebp            // 恢复上层函数栈顶指针  
  21.  pop         ebp                // 恢复上层函数栈底指针  
  22.  ret                            // 没有栈平衡操作  

 __cdecl是c/c++默认的调用方式。从上面的汇编代码可以看到,__cdecl调用方式在函数内没有任何平衡参数的操作,而在退出函数后对esp执行了加8(add esp,8)的操作。C语言中经常使用的printf函数就是典型的__cdecl调用方式,由于printf的参数可以有多个,所以只能以__cdecl方式调用。

(2)__stdcall调用约定

1. 参数从右向左传递,放在栈中

2. 栈平衡操作由被调用函数执行

3. 不定参数的函数无法使用

看一下汇编的例子

  1. int a = 1, b = 2;  
  2. mov         dword ptr [a],1    
  3. mov         dword ptr [b],2    
  4. int sum = Sum(a, b);  
  5. mov         eax,dword ptr [b]    // 参数从右向左压入栈,压入参数b  
  6. push        eax    
  7. mov         ecx,dword ptr [a]    // 参数从右向左压入栈,压入参数a  
  8. push        ecx    
  9. call        Sum (13611C2h)    
  10. mov         dword ptr [sum],eax  // 没有平衡栈操作,平衡操作由函数Sum内部完成  
  1. int __stdcall Sum(int a, int b)  
  2. {  
  3.  push        ebp    
  4.  mov         ebp,esp    
  5.  sub         esp,0C0h    
  6.  push        ebx    
  7.  push        esi    
  8.  push        edi    
  9.  lea         edi,[ebp-0C0h]    
  10.  mov         ecx,30h    
  11.  mov         eax,0CCCCCCCCh    
  12.  rep stos    dword ptr es:[edi]    
  13.     return a+b;  
  14.  mov         eax,dword ptr [a]    
  15.  add         eax,dword ptr [b]    
  16. }  
  17.  pop         edi    
  18.  pop         esi    
  19.  pop         ebx    
  20.  mov         esp,ebp    
  21.  pop         ebp    
  22.  ret         8                  // 平衡栈操作,栈弹出8个字节,等价于esp += 8  

__stdcall与__cdecl除了栈平衡任务由谁来完成以外,其余部分一样。

(3)__fastcall调用约定

1. 最左边的两个不大于4字节的参数分别放在ecx和edx寄存器,其余参数仍然从右到左压入栈

2. 被调用方平衡栈

3. 不定参数无法使用

例子:

  1. int a = 1, b = 2, c = 3;  
  2.  mov         dword ptr [a],1    
  3.  mov         dword ptr [b],2    
  4.  mov         dword ptr [c],3    
  5. int sum = Sum(a, b, c);  
  6.  mov         eax,dword ptr [c]    
  7.  push        eax    
  8.  mov         edx,dword ptr [b]    // 最左边的第二个参数由edx传递  
  9.  mov         ecx,dword ptr [a]    // 最左边的第一个参数由ecx传递  
  10.  call        Sum (0E611CCh)    
  11.  mov         dword ptr [sum],eax  // 没有平衡栈操作,平衡栈操作由被调用方完成  
  1. int __fastcall Sum(int a, int b, int c)  
  2. {  
  3.  push        ebp    
  4.  mov         ebp,esp    
  5.  sub         esp,0D8h    
  6.  push        ebx    
  7.  push        esi    
  8.  push        edi    
  9.  push        ecx    
  10.  lea         edi,[ebp-0D8h]    
  11.  mov         ecx,36h    
  12.  mov         eax,0CCCCCCCCh    
  13.  rep stos    dword ptr es:[edi]    
  14.  pop         ecx    
  15.  mov         dword ptr [ebp-14h],edx    
  16.  mov         dword ptr [ebp-8],ecx    
  17.     return a+b+c;  
  18.  mov         eax,dword ptr [a]    
  19.  add         eax,dword ptr [b]    
  20.  add         eax,dword ptr [c]    
  21. }  
  22.  pop         edi    
  23.  pop         esi    
  24.  pop         ebx    
  25.  mov         esp,ebp    
  26.  pop         ebp    
  27.  ret         4 // 平衡栈,4个字节,因为前两个参数是通过寄存器传递的,只有第三个参数压入栈中了  

但是,对于浮点值、远指针和__int64类型总是通过栈来传递。

(4) __thiscall调用约定

thiscall仅仅用于c++成员函数。this指针存放于ecx寄存器中,参数从右到左压栈,被调用方平衡栈。thiscall不是关键词不能被程序员指定。

例子:

  1. int a = 1, b = 2;  
  2. mov         dword ptr [a],1    
  3. mov         dword ptr [b],2    
  4. CTest test;  
  5. int sum = test.Sum(a, b);  
  6. mov         eax,dword ptr [b]    
  7. push        eax    
  8. mov         ecx,dword ptr [a]    
  9. push        ecx    
  10. lea         ecx,[test]             // 对象指针通过ecx传递  
  11. call        CTest::Sum (1911D1h)    
  12. mov         dword ptr [sum],eax    
  1. class CTest  
  2. {  
  3. public:  
  4.     int Sum(int a, int b)  
  5.     {  
  6.  push        ebp    
  7.  mov         ebp,esp    
  8.  sub         esp,0CCh    
  9.  push        ebx    
  10.  push        esi    
  11.  push        edi    
  12.  push        ecx    
  13.  lea         edi,[ebp-0CCh]    
  14.  mov         ecx,33h    
  15.  mov         eax,0CCCCCCCCh    
  16.  rep stos    dword ptr es:[edi]    
  17.  pop         ecx    
  18.  mov         dword ptr [ebp-8],ecx    
  19.         return a+b;  
  20.  mov         eax,dword ptr [a]    
  21.  add         eax,dword ptr [b]    
  22.     }  
  23.  pop         edi    
  24.  pop         esi    
  25.  pop         ebx    
  26.  mov         esp,ebp    
  27.  pop         ebp    
  28.  ret         8     // 平衡栈  

抱歉!评论已关闭.