我目前正在为Windows开发一个C++库,它将作为一个DLL发布。我的目标是最大化二进制互操作性;更准确地说,我的DLL中的函数必须可以从用多个版本的MSVC++和MinGW编译的代码中使用,而不必重新编译DLL。然而,我不知道哪个调用约定最好,cdecl还是stdcall。
有时,我会听到诸如“C调用约定是唯一保证是相同的交叉编译器”的语句,这与",尤其是如何返回值。“这样的语句形成了对比。这似乎并不能阻止某些库开发人员(如libsndfile)在他们分发的DLL中使用C调用约定,而不会出现任何明显的问题。
另一方面,stdcall调用约定似乎有很好的定义.据我所知,所有Windows编译器基本上都需要遵循它,因为这是用于Win32和COM的惯例。这是基于这样的假设,即没有Win32 32/COM支持的Windows编译器不会很有用。论坛上发布的许多代码片段将函数声明为stdcall,但我似乎找不到一个帖子,这清楚地解释了原因。
有太多冲突的信息,每次我运行的搜索都给出了不同的答案,这并不能真正帮助我在两者之间做出决定。我正在寻找一个清晰、详细、有争议的解释,解释为什么我应该选择一个而不是另一个(或者为什么两者是等价的)。
请注意,这个问题不仅适用于“经典”函数,而且也适用于虚拟成员函数调用,因为大多数客户端代码将通过“接口”(纯虚拟类)与我的DLL接口(以下模式描述了这里和那里)。
发布于 2011-07-09 13:37:25
我只是做了一些真实的测试(用MSVC++和MinGW编译DLL和应用程序,然后混合它们)。从表面上看,我对cdecl调用约定有更好的结果。
更具体地说:stdcall的问题是MSVC++在DLL导出表中损坏名称,即使使用extern "C"也是如此。例如,foo变成了_foo@4。只有在使用__declspec(dllexport)时才会发生这种情况,而不是在使用DEF文件时;但是,在我看来,DEF文件是维护上的麻烦,我不想使用它们。
MSVC++名称的损坏造成了两个问题:
GetProcAddress变得稍微复杂一些;foo@4而不是_foo@4)之前加上一个undescore,这会使链接复杂化。此外,它还介绍了在野外出现与“下划线版本”不兼容的DLL和应用程序“非下划线版本”的风险。我已经尝试过cdecl约定: MSVC++和MinGW之间的互操作性工作得非常完美,开箱即用,名称在DLL导出表中不加修饰。它甚至适用于虚拟方法。
出于这些原因,cdecl显然是我的赢家。
发布于 2011-06-28 18:19:26
两个调用约定的最大区别是,"____cdecl“将在调用方调用函数之后平衡堆栈的负担放在调用方身上,这允许具有可变数量参数的函数。"__stdcall“公约在性质上是”更简单“的,尽管在这方面不那么灵活。
而且,我认为托管语言默认使用stdcall约定,所以如果您使用cdecl,任何在其上使用P/Invoke的人都必须显式地声明调用约定。
因此,如果您的所有函数签名都是静态定义的,我可能倾向于stdcall,如果不是cdecl的话。
发布于 2014-01-02 10:50:53
在安全性方面,__cdecl约定“更安全”,因为需要释放堆栈的是调用方。在__stdcall库中可能发生的情况是,开发人员可能忘记正确释放堆栈,或者攻击者可能通过破坏DLL的堆栈(例如通过API挂钩)注入一些代码,然后调用方不检查这些代码。我没有任何CVS的安全例子来证明我的直觉是正确的。
https://stackoverflow.com/questions/6511096
复制相似问题