总结&备忘:__stdcall,__cdecl,extern "C"

原创 2016年03月09日 13:39:22

昨天给朋友解答了一下ESP异常的问题,才想起最近一年来都没怎么碰过C和WIN32开发了,怕再久一点再老一点就忘光了,还是得记录一下.

一、先解释一下__stdcall,__cdecl的作用

__stdcall,__cdecl,__fastcall,...这些是函数调用时参数传递方式,入栈顺序,怎么还原栈和函数在可链接模块中的命名的协议。VS编译环境里默认的协议是__stdcall

__stdcall协议为:

1.调用者把参数从右向左逐个入栈;

2.该栈由被调用者还原;

优点:为了节省汇编代码? 试想栈由调用者还原的话,那必定每次调用后都要写一份还原代码,调N次就会有N份还原栈代码

缺点:不支持可变参数. 试想若是可变参,那只有调用者知道压了多少个参数进栈(没另找方法告诉被调用者的情况下),这情况下被调用者怎么能知道要还原多大的栈空间呢

3.命名里追加参数信息

优点:实现函数重载; 

缺点:减慢编译速度...好吧...我偏执了,另一个缺点虽不是天生的,但现实存在,即不同C++编译器有不同的命名规范,导致互不兼容

__cdecl协议为:

1.调用者把参数从右向左逐个入栈;

2.该栈由调用者还原;

优点:支持可变参数; 

缺点:多次调用比stdcall更耗代码量)

3.只在函数名前加下划线'_'

优点:各编译器一致实现,相互兼容

缺点:不支持函数重载(姑且算上是缺点吧...)

二、ESP异常最常见的原因是在VS里用默认的__stdcall方式调用了用动态库里用__cdecl方式实现的函数

异常窗口信息:"The value of ESP was not properly saved across a function call..."大致意思系栈指针不正确,可能是由于不同函数调用协议混用导致的

有部分第三方C库用的是__cdecl协议编译的,你若在VS中动态加载时使用如下函数指针去指向该库中的函数:

typedef void (* Foo)(int a);

则会出现ESP异常.原因就是VS中默认为__stdcall协议,在这里,就是调用者使用__stdcall协议,被调用者使用__cdecl协议,则出现,调用者把参数压进栈后,直至函数调用完成,都没有进行栈的还原(即清理),因为被调用者是遵循__cdecl协议所以被调用者不还原栈,同时调用者又遵循__stdcall所以调用者也不还原栈.最终谁也没还原栈,导致栈的不平衡,触发了ESP检查异常.

故此,解决问题的方法就很明显了,函数指针加多__cdecl描述就可以了:

typedef void(_cdecl * FooI)(int a);


三、我朋友那竟然不是上述问题导致的

他是将第三方的源码和头文件拷进VS2013工程里直接编译的,头文件和实现文件里都没加任何描述符,那就都是默认一致的(这里为默认的__stdcall).

后来他自己发现他头文件里的声明是void fun(int a)

而实现文件里实现的却是void fun(int a,int b){}

把头文件里的声明修正为void fun(int a,int bI)后,正常运行.

我开始时就想,这报ESP也讲得通,调用者只压了一个int进栈,而被调用者还原栈时弹出了两个int,也会导致栈的不平衡,我就这么认为了

但过了几分钟回想了一遍...WTF!!!这不对啊,头文件声明错了应该是报"找不到该函数的实现"才对啊

难道我过去一年没弄VS,编译器发生了翻天覆地的变化???于是怀疑是C++11标准的新特性,简单翻了一下也查不到,只查到一个模板化的可变参数.想想也不可能是新特性,新特性是个会弹ESP异常的是什么鬼啊...或许是VS2013编译器里的BUG吧

身边现在没windows环境,暂不深入研究了

版权声明:本文为博主原创文章,如要转载请标明出处。

_stdcall的作用

并不是所有的语言都支持_cdcel调用规则,但是都支持_sdtcall调用规则, 假如你用VC做了一个DLL,导出了某些函数,如果你想这个DLL被其他语言也能调用的话,VB.DEPHI.PB..你的把...
  • u4110122855
  • u4110122855
  • 2012年12月06日 19:54
  • 8128

__stdcall的理解与作用

在做一个系统集成的项目中,遇到typedef int(__stdcall *Func_*****_System_Version)();这样的声明,第一次接触_stacall,故整理如下: 转载:ht...
  • xk_snail
  • xk_snail
  • 2015年08月23日 21:42
  • 568

DLL编写中extern “C”和__stdcall的作用

动态链接库的使用有两种方式,一种是显式调用。一种是隐式调用。 (1)       显式调用:使用LoadLibrary载入动态链接库、使用GetProcAddress获取某函数地址。 (2...
  • dongchongyang
  • dongchongyang
  • 2016年10月25日 19:35
  • 3634

__stdcall,__cdecl,_cdecl,_stdcall,__fastcall,_fastcall 区别简介 和 extern "C" 的作用

今天写线程函数时,发现msdn中对ThreadProc的定义有要求:DWORD WINAPI ThreadProc(LPVOID lpParameter); 不解为什么要用WINAPI宏定义,查...
  • qq_28337005
  • qq_28337005
  • 2016年05月29日 17:00
  • 106

extern "C" 与 __stdcall使用

xtern "C" __declspec (dllexport) char* __stdcall SQLFetch(const char* fieldname); C語言中extern c c与c...
  • huapeng_guo
  • huapeng_guo
  • 2012年06月18日 09:53
  • 4927

DLL编写中extern “C”和__stdcall的作用

动态链接库的使用有两种方式,一种是显式调用。一种是隐式调用。 (1)       显式调用:使用LoadLibrary载入动态链接库、使用GetProcAddress获取某函数地址。 (2)   ...
  • kxykkk
  • kxykkk
  • 2014年05月23日 14:37
  • 1933

何时使用WINAPI,CALLBACK 以及_stdcall _cdecl _pascal 等的用法区别总结

#define   CALLBACK         __stdcall  #define   WINAPI             __stdcall  #define   WINAPIV ...
  • u013501457
  • u013501457
  • 2014年05月18日 18:19
  • 1297

完全总结__cdecl __fastcall, __stdcall,__thiscall

__cdecl __fastcall, __stdcall,__thiscall它们都是调用约定(Calling convention),它决定以下内容: 1)函数参数的压栈顺序 2)由调用者还是...
  • thefutureisour
  • thefutureisour
  • 2012年11月06日 10:50
  • 1126

cdecl、stdcall,pascal三种动态库的区别

调用约定(Calling   convention):决定函数参数传送时入栈和出栈的顺序,由调用者还是被调用者把参数弹出栈,以及编译器用来识别函数名字的修饰约定。函数调用约定有多种,这里简单说一下: ...
  • deerleaper
  • deerleaper
  • 2014年09月30日 14:34
  • 744

__stdcall 与 __cdecl 区别 (汇编 call ret 时,栈的变化)

__cdecl C++ void fun(int a)   //默认__cdecl {  cout } int main() {  fun(3);  system("pause");  ret...
  • WMJ75617718
  • WMJ75617718
  • 2014年03月17日 00:35
  • 1369
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:总结&备忘:__stdcall,__cdecl,extern "C"
举报原因:
原因补充:

(最多只允许输入30个字)