__stdcall与__cdecl函数调用的不同

参考:

http://blog.csdn.net/hudashi/article/details/7820338

http://shitou7630.blog.163.com/blog/static/32699536201342110155436/

http://www.cnblogs.com/52yixin/archive/2011/06/29/2093634.html

http://blog.csdn.net/mniwc/article/details/7993361

http://www.cnblogs.com/coderzh/archive/2008/12/01/1345053.html

http://blog.sina.com.cn/s/blog_6f6769b50100uhzz.html

https://msdn.microsoft.com/zh-cn/library/ms235286.aspx

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源)

ESP是栈顶指针   Extended Stack Pointer

EBP是基址指针   Extend Base Pointer

ESP就是一直指向栈顶的指针,而EBP只是用于存取某时刻的栈顶指针,以方便对栈的操作


(win32下) 使用__stdcall的函数定义和函数翻译


void __stdcall TestCall(int a, int b)

{

return;

}

执行

TestCall(1, 2);

使用__stdcall的函数翻译:  (执行时转到”反汇编”窗口)


TestCall(1, 2);

00F013FE  push        2

00F01400  push        1

00F01402  call        TestCall (0F01177h)

void __stdcall TestCall(int a, int b)

{

return;

}

00F013B0  push        ebp         //
压栈

00F013B1  mov         ebp,esp     //
保存esp到ebp

00F013B3  sub         esp,0C0h

00F013B9  push        ebx

00F013BA  push        esi

00F013BB  push        edi

00F013BC  lea         edi,[ebp-0C0h]

00F013C2  mov         ecx,30h

00F013C7  mov         eax,0CCCCCCCCh

00F013CC  rep stos    dword ptr es:[edi]

00F013CE  pop         edi

00F013CF  pop         esi

00F013D0  pop         ebx

00F013D1  mov         esp,ebp     //
从ebp还原esp

00F013D3  pop         ebp         //
出栈还原ebp

00F013D4  ret         8           //
返回并退栈8字节,效果等同 add esp, 8; ret;

(注意,压栈时esp地址变小,出栈时esp地址变大,因为是从后往前分配的。)


(win32下) 使用__cdecl或缺省的函数定义和函数翻译


void __stdcall TestCall(int a, int b)

{

return;

}

执行

TestCall(1, 2);

使用__cdecl或缺省的函数翻译:  (执行时转到”反汇编”窗口)


TestCall(1, 2);

012B13FE  push        2

012B1400  push        1

012B1402  call        TestCall (12B11D6h)

012B1407  add         esp,8       // esp退栈8字节,把push 2; push 1;压入的8字节退掉。

void TestCall(int a, int b)

{

return;

}

00F013B0  push        ebp         //
压栈

00F013B1  mov         ebp,esp     //
保存esp到ebp

00F013B3  sub         esp,0C0h

00F013B9  push        ebx

00F013BA  push        esi

00F013BB  push        edi

00F013BC  lea         edi,[ebp-0C0h]

00F013C2  mov         ecx,30h

00F013C7  mov         eax,0CCCCCCCCh

00F013CC  rep stos    dword ptr es:[edi]

00F013CE  pop         edi

00F013CF  pop         esi

00F013D0  pop         ebx

00F013D1  mov         esp,ebp     //
从ebp还原esp

00F013D3  pop         ebp         //
出栈还原ebp

00F013D4  ret                     //
直接返回,不进行退栈操作

l  从上面可以看出

使用__stdcall的话,调用侧从右往左压栈函数参数,但不退栈,需要函数退栈

(ret 8  -- esp退栈8个字节)

使用__cdecl的话,调用侧从右往左压栈函数参数,并且在调用后,调用侧主动执行退栈 (add esp, 8  --  esp退栈8个字节)

l  不用退栈的情况

有一些情况,由于不用退栈,调用他人声明的函数时用哪种都不会报错

无参数的情况:

这种情况,由于调用侧未压栈函数参数,所以不存在函数参数的退栈问题,退栈也只需退0长度

winX64位情况下编译,当参数少于4个时,未进行压栈:

参考:

https://msdn.microsoft.com/zh-cn/library/ms235286.aspx

http://blog.sina.com.cn/s/blog_6f6769b50100uhzz.html

http://openwares.net/misc/windows_x64_function_call_convention.html

前四个参数被放入到寄存器中,而不是压入堆栈中的,同无参数一样,也不存在退栈情况。

(通常参数在寄存器 RCX、RDX、R8
和 R9 中传递。)

例如


void __stdcall TestCall(int& a, int& b)

{

return;

}

int a = 1;

int b = 2;

TestCall(a, b);

TestCall(a, b);

000000013F031247  mov         edx,dword ptr [b]

000000013F03124B  mov         ecx,dword ptr [a]

000000013F03124F  call        TestCall (13F031028h)

void __stdcall TestCall(int& a, int& b)

{     return;

}

000000013F0311B0  mov         dword ptr [rsp+10h],edx

000000013F0311B4  mov         dword ptr [rsp+8],ecx

000000013F0311B8  push        rdi

000000013F0311B9  pop         rdi

000000013F0311BA  ret

Gcc编译时,更加不同,这里不作详细介绍:

参考 http://blog.sina.com.cn/s/blog_6f6769b50100uhzz.html

“win_hate”: 发现一般规则为,当参数6个及6个以内时,参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。当参数为
6个以上时,前 6 个与前面一样,但后面的依次从 "右向左放入栈中。

l  其它说明

参考文档中对这两种调用的说明:

http://www.cnblogs.com/coderzh/archive/2008/12/01/1345053.html


1、_stdcall是Pascal程序的缺省调用方式,通常用于Win32
Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。 int f(void *p) -->> [email protected](在外部汇编语言里可以用这个名字引用这个函数)

2、C调用约定(即用__cdecl关键字说明)(The C default
calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数 vararg的函数(如printf)只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。
_cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源)

时间: 2024-10-14 08:47:05

__stdcall与__cdecl函数调用的不同的相关文章

__stdcall、__cdecl、CALLBACK、WINAPI区别

情景一:先看下Windows开发下的使用: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 这里就有一个CALLBACK,转到定义看一下 #elif (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) #define CALLBACK __stdcall #define WINAPI __stdcall ... 从上面也可以看到在windows平台下WINAPI和CALLBCAK其实是一致的

C/C++:函数调用规则__stdcall,__cdecl,__pascal,__fastcall

__cdecl __cdecl 是 C Declaration  的缩写,表示 C 语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈.被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误. __stdcall __stdcall 是 Standard Call 的缩写,是 C++ 的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是 this 指针.这些堆栈中的参数由被调用的

__stdcall 与 __cdecl

(1) _stdcall调用 _stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈. WIN32 Api都采用_stdcall调用方式,这样的宏定义说明了问题: #define WINAPI _stdcall 按C编译方式,_stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如. (2) _cdecl调用 _cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护._ced

__declspec,__cdecl,__stdcall区别和作用

_cdecl和__stdcall都是函数调用规范(还有一个__fastcall),规定了参数出入栈的 顺序和方法,如果只用VC编程的话可以不用关心,但是要在C++和Pascal等其他语言通信的时候就要注意了,只有用相同的方法才能够调用成功.另外, 像printf这样接受可变个数参数的函数只有用cdecl才能够实现.      __declspec主要是用于说明DLL的引出函数的,在某些情况下用__declspec(dllexport)在DLL中生命引出函数,比用传统的 DEF文件方便一些.在普通

__cdecl、__stdcall、__fastcall 与 __pascal 浅析

call 指令与 retn 指令 首先我们得了解 CALL 和 RETN 指令的作用,才能更好地理解调用规则,这也是先决条件. 实际上,CALL 指令就是先将下一条指令的 EIP 压栈,然后 JMP 跳转到对应的函数的首地址,当执行完函数体后,通过 RETN 指令从堆栈中弹出 EIP,程序就可以继续执行 CALL 的下一条指令. __cdecl 与 __stdcall 调用规则 C/C++ 中不同的函数调用规则会生成不同的机器代码,产生不同的微观效果,接下来让我们一起来浅析四种调用规则的原理和它

C调用约定__cdecl、__stdcall、__fastcall、__pascal分析

参考原文地址:https://www.cnblogs.com/yenyuloong/p/9626658.html C/C++ 中不同的函数调用规则会生成不同的机器代码,产生不同的微观效果,接下来让我们一起来浅析四种调用规则的原理和它们各自的异同.通过一段 C 语言代码来引导我们的浅析过程.这里我们编写了三个函数,它们的功能都是返回两个参数的相加结果,只是每个函数都有不一样的调用规则. 用 OllyDBG 查看了编译后代码的“真面目”.代码中有 4 个 CALL,第一个是 printf,我们不关心

__stdcall,__cdecl,__fastcall的区别

__stdcall,__cdecl,__fastcall的区别 标签: dll编译器pascalclassimportinitialization 2009-12-09 15:07 10472人阅读 评论(1) 收藏 举报 分类: C/C++(22) __stdcall,__cdecl,__fastcall的区别 一.三者区别一览表 __stdcall __cdecl __fastcall 参数传递方式 右->左 压栈 右->左 压栈 左边开始的两个不大于4字节(DWORD)的参数分别放在EC

深入体会__cdecl与__stdcall

在学习C++的过程中时常碰到WINAPI或者CALLBACK这样的调用约定,每每觉得十分迷惑.究竟这些东西有什么用?不用他们又会不会有问题?经过在网上的一番搜寻以及自己动手后,整理成以下的学习笔记.1.WINAPI与CALLBACK 其实这两者在Windows下是相同的,在windef.h中定义如下: #ifdef _MAC#define CALLBACK    PASCAL#define WINAPI      CDECL#define WINAPIV     CDECL#define API

[参考]__stdcall、__cdcel和__fastcall三者的区别

一.概述 __stdcall.__cdecl和__fastcall是三种函数调用协议,函数调用协议会影响函数参数的入栈方式.栈内数据的清除方式.编译器函数名的修饰规则等. 二.调用协议常用场合  __stdcall:Windows API默认的函数调用协议. __cdecl:C/C++默认的函数调用协议. __fastcall:适用于对性能要求较高的场合. 三. 函数参数入栈方式  __stdcall:函数参数由右向左入栈. __cdecl:函数参数由右向左入栈. __fastcall:从左开始