我是靠谱客的博主 明亮柠檬,最近开发中收集的这篇文章主要介绍C/C++函数调用约定(基于X86架构下),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

__cdecl约定

C/C++中缺省调用约定

int add(int a, int b)
{
00D91002  in          al,dx  
00D91003  push        ecx  
    int c = a + b;
00D91004  mov         eax,dword ptr [a]  
00D91007  add         eax,dword ptr [b]  
00D9100A  mov         dword ptr [c],eax  
    return c;
00D9100D  mov         eax,dword ptr [c]  
}

int main()
{
00D91020  push        ebp  
00D91021  mov         ebp,esp  
    add(3, 4);
00D91023  push        4  
00D91025  push        3  
00D91027  call        add (0D91000h)  
00D9102C  add         esp,8 //恢复栈平衡 
}

参数从右到左入栈,在函数外恢复栈平衡,返回值在EAX中
由于由调用者平衡栈,所以允许可变参数函数存在

__stdcall约定

常见于WINAPI中

int __stdcall add(int a, int b)
{
00641002  in          al,dx  
00641003  push        ecx  
    int c = a + b;
00641004  mov         eax,dword ptr [a]  
00641007  add         eax,dword ptr [b]  
0064100A  mov         dword ptr [c],eax  
    return c;
0064100D  mov         eax,dword ptr [c]  
}
00641010  mov         esp,ebp  
00641012  pop         ebp  
00641013  ret         8//恢复栈平衡
    
int main()
{
00641020  push        ebp  
00641021  mov         ebp,esp  
    add(3, 4);
00641023  push        4  
00641025  push        3  
00641027  call        add (0641000h)  
}

参数入栈方式与__cdecl相同;不同于__cdecl,由函数恢复栈平衡;返回值在eax中

__thiscall约定

C++中用于调用成员函数

class Math
{
public:
    int add(int a, int b)
    {
002E1002  in          al,dx  
002E1003  sub         esp,8  
002E1006  mov         dword ptr [ebp-8],ecx  
        int c = a + b;
002E1009  mov         eax,dword ptr [ebp+8]  
002E100C  add         eax,dword ptr [ebp+0Ch]  
002E100F  mov         dword ptr [ebp-4],eax  
        return c;
002E1012  mov         eax,dword ptr [ebp-4]  
    }
002E1015  mov         esp,ebp  
002E1017  pop         ebp  
002E1018  ret         8 
    
int main()
{
002E1020  push        ebp  
002E1021  mov         ebp,esp  
002E1023  sub         esp,8  
002E1026  mov         eax,dword ptr ds:[002E3000h]  
002E102B  xor         eax,ebp  
002E102D  mov         dword ptr [ebp-4],eax  
    Math math;
    math.add(3, 4);
002E1030  push        4  
002E1032  push        3  
002E1034  lea         ecx,[ebp-5]  
002E1037  call        002E1000  
}

与__stdcall相同,由函数自身恢复栈平衡;
从右向左将参数传入栈中,ecx保存类的指针

__fastcall约定

调用成员函数时

class Math
{
public:
    int __fastcall add(int a, int b)
    {
00501002  in          al,dx  
00501003  sub         esp,0Ch  
00501006  mov         dword ptr [ebp-4],edx  
00501009  mov         dword ptr [ebp-0Ch],ecx  
        int c = a + b;
0050100C  mov         eax,dword ptr [ebp-4]  
0050100F  add         eax,dword ptr [ebp+8]  
00501012  mov         dword ptr [ebp-8],eax  
        return c;
00501015  mov         eax,dword ptr [ebp-8]  
    }
00501018  mov         esp,ebp  
0050101A  pop         ebp  
0050101B  ret         4

    int main()
{
00501020  push        ebp  
00501021  mov         ebp,esp  
00501023  sub         esp,8  
00501026  mov         eax,dword ptr ds:[00503000h]  
0050102B  xor         eax,ebp  
0050102D  mov         dword ptr [ebp-4],eax  
    Math math;
    math.add(3, 4);
00501030  push        4  
00501032  mov         edx,3  
00501037  lea         ecx,[ebp-5]  
0050103A  call        00501000  
}

调用普通函数时

int __fastcall add(int a, int b)
    {
00391002  in          al,dx  
00391003  sub         esp,0Ch  
00391006  mov         dword ptr [ebp-8],edx  
00391009  mov         dword ptr [ebp-4],ecx  
        int c = a + b;
0039100C  mov         eax,dword ptr [ebp-4]  
0039100F  add         eax,dword ptr [ebp-8]  
00391012  mov         dword ptr [ebp-0Ch],eax  
        return c;
00391015  mov         eax,dword ptr [ebp-0Ch]  
    }
00391018  mov         esp,ebp  
0039101A  pop         ebp  
0039101B  ret

    
int main()
{
00391020  push        ebp  
00391021  mov         ebp,esp  
   
   add(3, 4);
00391023  mov         edx,4  
00391028  mov         ecx,3  
0039102D  call        00391000  
}

优先使用寄存器ECX和EDX传送前两个DWORD或更小参数,节省push操作,因此速度较快

其他约定

___naked
naked是一个很少见的调用约定,一般不建议使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是你不能用return返回返回值,只能用插入汇编返回结果,此调用约定必须跟_declspec同时使用。例如定义一个求和程序,如:_declspec (naked) int add(int a,int b) ;

___pascal
这是pascal语言的调用约定,跟_stdcall一样,参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在EAX中。VS 中已经废弃了这种调用方式.

最后

以上就是明亮柠檬为你收集整理的C/C++函数调用约定(基于X86架构下)的全部内容,希望文章能够帮你解决C/C++函数调用约定(基于X86架构下)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(72)

评论列表共有 0 条评论

立即
投稿
返回
顶部