3种调用约定的区别与联系

由C代码到汇编代码来看看cdecl、stdcall、fastcall三个调用约定的区别:

int __stdcall add1(int x,int y)
{
    return x + y;
}
int __cdecl add2(int x, int y)
{
    return x + y;
}
int __fastcall add3(int x, int y,int z)
{
    return x + y +z;
}

int _tmain(int argc, _TCHAR* argv[])
{
    add1(1,2);
    add2(1, 2);
    add3(1, 2, 3);
    return 0;
}

main函数里面的反汇编代码:

int _tmain(int argc, _TCHAR* argv[])
{
001B14A0  push        ebp
001B14A1  mov         ebp,esp
001B14A3  sub         esp,0C0h
001B14A9  push        ebx
001B14AA  push        esi
001B14AB  push        edi
001B14AC  lea         edi,[ebp-0C0h]
001B14B2  mov         ecx,30h
001B14B7  mov         eax,0CCCCCCCCh
001B14BC  rep stos    dword ptr es:[edi]
    add1(1,2);
001B14BE  push        2
001B14C0  push        1
001B14C2  call        add1 (01B1032h)
    add2(1, 2);
001B14C7  push        2
001B14C9  push        1
001B14CB  call        add2 (01B102Dh)
001B14D0  add         esp,8
    add3(1, 2, 3);
001B14D3  push        3
001B14D5  mov         edx,2
001B14DA  mov         ecx,1
001B14DF  call        add3 (01B100Ah)
    return 0;
001B14E4  xor         eax,eax
}
001B14E6  pop         edi
001B14E7  pop         esi
001B14E8  pop         ebx
001B14E9  add         esp,0C0h
001B14EF  cmp         ebp,esp
001B14F1  call        __RTC_CheckEsp (01B114Fh)
001B14F6  mov         esp,ebp
001B14F8  pop         ebp
001B14F9  ret

对照源码,从表面上看,stdcall和fastcall传递参数方式为从右至左,由自身清理堆栈。

int __stdcall add1(int x,int y)
{
001B13D0  push        ebp
001B13D1  mov         ebp,esp
001B13D3  sub         esp,0C0h
001B13D9  push        ebx
001B13DA  push        esi
001B13DB  push        edi
001B13DC  lea         edi,[ebp-0C0h]
001B13E2  mov         ecx,30h
001B13E7  mov         eax,0CCCCCCCCh
001B13EC  rep stos    dword ptr es:[edi]
    return x + y;
001B13EE  mov         eax,dword ptr [x]
001B13F1  add         eax,dword ptr [y]
}
001B13F4  pop         edi
001B13F5  pop         esi
001B13F6  pop         ebx
001B13F7  mov         esp,ebp
001B13F9  pop         ebp
001B13FA  ret         8        //自身清理堆栈

而cdecl为从右至左传递参数,由调用者清理堆栈。

001B14C7  push        2
001B14C9  push        1
001B14CB  call        add2 (01B102Dh)
001B14D0  add         esp,8

fastcall传递参数很快,适合大程序的优化,前两个由EDX和ECX来传递,多出来的用push方式传递参数:

001B14D3  push        3
001B14D5  mov         edx,2
001B14DA  mov         ecx,1
001B14DF  call        add3 (01B100Ah)
int __fastcall add3(int x, int y,int z)
{
001B1450  push        ebp
001B1451  mov         ebp,esp
001B1453  sub         esp,0D8h
001B1459  push        ebx
001B145A  push        esi
001B145B  push        edi
001B145C  push        ecx
001B145D  lea         edi,[ebp-0D8h]
001B1463  mov         ecx,36h
001B1468  mov         eax,0CCCCCCCCh
001B146D  rep stos    dword ptr es:[edi]
001B146F  pop         ecx          //由于先借由ecx来初始化,所以先push保存,后pop恢复
001B1470  mov         dword ptr [y],edx
001B1473  mov         dword ptr [x],ecx
    return x + y + z;
001B1476  mov         eax,dword ptr [x]
001B1479  add         eax,dword ptr [y]
001B147C  add         eax,dword ptr [z]
}
001B147F  pop         edi
001B1480  pop         esi
001B1481  pop         ebx
001B1482  mov         esp,ebp
001B1484  pop         ebp
001B1485  ret         4      //由自身清理堆栈
时间: 2024-10-12 13:11:04

3种调用约定的区别与联系的相关文章

64位只有一种调用约定stdcall

procedure TForm2.Button1Click(Sender: TObject); function EnumWindowsProc(Ahwnd: hwnd; AlParam: lParam): Boolean; stdcall; begin ShowMessage('hwnd:' + IntToStr(Ahwnd)); ShowMessage('lParam' + IntToStr(AlParam)); Result := True; end; begin EnumChildWin

在win64里,只有一种调用约定

在win64里,只有一种调用约定.以下是通过寄存器来传递4个整数类型的例子: *RCX:第一个参数 *RDX:第二个参数 *R8:第三个参数 *R9:第四个参数 参数里开头的4个整数会这样传给栈.传递浮点数参数时,使用的是XMMO-XMM3寄存器. 调用约定简化了:一律使用__fastcall,前四个参数用 RCX.RDX.R8 和 R9传递,除了这四个外加RAX.R10.R11,其他寄存器都是非易失的.

宏WINAPI和几种调用约定

在VC SDK的WinDef.h中,宏WINAPI被定义为__stdcall,这是C语言中一种调用约定,常用的还有__cdecl和__fastcall.这些调用约定会对我们的代码产生什么样的影响?让我们逐个分析. 首先,在x86平台上,用VC编译这样一段代码: 1 int __cdecl TestC(int n0, int n1, int n2, int n3, int n4, int n5) 2 { 3 int n = n0 + n1 + n2 + n3 + n4 + n5; 4 return

几种调用约定

调用约定(Callingconvention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法.MFC支持以下调用约定: 1._cdecl 按从右至左的顺序压参数入栈,由调用者把参数弹出栈.对于"C"函数或者变量,修饰名是在函数名前加下划线.对于"C++"函数,有所不同. 如函数voidtest(void)的修饰名是_test:对于不属于一个类的"C++"全局函数,修饰名是[email protecte

html5 中的 css样式单 的 两种调用方式的区别

在 html5 中 使用 css 样式单的方式 有4种: 1.链接外部样式文件:将样式文件 彻底与 html 文档分离,样式文件需要额外引入,这种情况下 一批样式 可以控制多份文档.对于好多文件都共有的样式单,推荐使用这种方式. 2.导入外部样式文件:此方式与第一种方式类似,但是需要用@import来引入外部样式单.由于某些浏览器(如 internet explorer)会在导入外部样式单时导致闪屏,所以不推荐用这种方式,而是尽量考虑使用第一种方式. 3.使用内部样式定义:这种方式是通过在htm

【系统篇】小议三种函数调用约定

小议三种函数调用约定 __cdecl.__stdcall.__fastcall是C/C++里中经常见到的三种函数调用方式.其中__cdecl是C/C++默认的调用方式,__stdcall是windows API函数的调用方式,只不过我们在头文件里查看这些API的声明的时候是用了WINAPI的宏进行代替了,而这个宏其实就是__stdcall了. 三种调用方式的区别相信大家应该有些了解,这篇文章主要从实例和汇编的角度阐述这些区别的表现形态,使其对它们的区别认识从理论向实际过渡. __cdecl: C

三种函数调用约定

__cdecl.__stdcall.__fastcall是C/C++里中经常见到的三种函数调用方式.其中__cdecl是C/C++默认的调用方式,__stdcall是windows API函数的调用方式,只不过我们在头文件里查看这些API的声明的时候是用了WINAPI的宏进行代替了,而这个宏其实就是__stdcall了. __cdecl: C/C++默认方式,参数从右向左入栈,主调函数负责栈平衡. __stdcall:            windows API默认方式,参数从右向左入栈,被调

DLL中调用约定和名称修饰(一)

DLL中调用约定和名称修饰(一) 调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议.这种协议规定了该语言的函数中的参数传送方式.参数是否可变和由谁来处理堆栈等问题.不同的语言定义了不同的调用约定. 在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链接器.这项技术通常被称为名称改编(Name Manglin

x86 x64下调用约定浅析

x86平台下调用约定 我们都知道x86平台下常用的有三种调用约定,__cdecl.__stdcall.__fastcall.我们分别对这三种调用约定进行分析. __cdecl __cdecl是C/C++的默认调用约定,如果不显示声明调用约定的情况下,就是该调用约定.下面我们来从汇编层次来熟悉这种调用约定. 我写了一个函数,如下: 1 int __cdecl TestCdecl(int a, int b, int c, int d, int e) 2 { 3 return a + b + c +