在 Windows 上是 cdecl 还是__ stdcall?

我目前正在开发一个用于 Windows 的 C + + 库,它将以 DLL 的形式发布。我的目标是最大限度地提高二进制互操作性; 更准确地说,我的 DLL 中的函数必须可以从使用多个版本的 MSVC + + 和 MinGW 编译的代码中使用,而不必重新编译 DLL。然而,我不知道哪种调用约定是最好的,cdecl还是 stdcall

有时我听到“ C 调用约定是唯一一个保证在不同编译器之间相同的约定”这样的语句,这与“ cdecl的解释有一些变化,特别是在如何返回值方面”这样的语句形成了对比。这似乎没有阻止某些库开发人员(如 Libsndfile)在他们分发的 DLL 中使用 C 调用约定,而且没有任何可见的问题。

另一方面,stdcall调用约定似乎是定义良好的。据我所知,所有的 Windows 编译器基本上都必须遵循它,因为这是 Win32和 COM 的约定。这是基于这样的假设: 没有 Win32/COM 支持的 Windows 编译器将不会非常有用。论坛上贴出的很多代码片段都将函数声明为 stdcall,但我似乎找不到一篇明确解释 为什么的文章。

有太多相互矛盾的信息,每次搜索我都会得到不同的答案,这并不能真正帮助我在两者之间做出选择。我正在寻找一个清晰的、详细的、有争议的解释,来解释为什么我应该选择一个而不是另一个(或者为什么两者是等价的)。

请注意,这个问题不仅适用于“经典”函数,而且也适用于虚拟成员函数调用,因为大多数客户端代码将通过“接口”,即纯虚拟类(如 给你那里所描述的模式)与我的 DLL 接口。

56799 次浏览

The biggest difference in the two calling conventions is that "__cdecl" places the burden of balancing the stack after a function call on the caller, which allows for functions with variable amounts of arguments. The "__stdcall" convention is "simpler" in nature, however less flexible in this regard.

Also, I believe managed languages use stdcall convention by default, so anyone using P/Invoke on it would have to explicitly state the calling convention if you go with cdecl.

So, if all of your function signatures are going to be statically defined I would probably lean toward stdcall, if not cdecl.

I just did some real-world testing (compiling DLLs and applications with MSVC++ and MinGW, then mixing them). As it appears, I had better results with the cdecl calling convention.

More specifically: the problem with stdcall is that MSVC++ mangles names in the DLL export table, even when using extern "C". For example foo becomes _foo@4. This only happens when using __declspec(dllexport), not when using a DEF file; however, DEF files are a maintenance hassle in my opinion, and I don't want to use them.

The MSVC++ name mangling poses two problems:

  • Using GetProcAddress on the DLL becomes slightly more complicated;
  • MinGW by default doesn't prepend an undescore to the decorated names (e.g. MinGW will use foo@4 instead of _foo@4), which complicates linking. Also, it introduces the risk of seeing "non-underscore versions" of DLLs and applications pop up in the wild which are incompatible with the "underscore versions".

I've tried the cdecl convention: interoperability between MSVC++ and MinGW works perfectly, out-of-the-box, and names stay undecorated in the DLL export table. It even works for virtual methods.

For these reasons, cdecl is a clear winner for me.

In terms of security, the __cdecl convention is "safer" because it is the caller that needs to deallocate the stack. What may happen in a __stdcall library is that the developer might have forgotten to deallocate the stack properly, or an attacker might inject some code by corrupting the DLL's stack (e.g. by API hooking) which is then not checked by the caller. I don't have any CVS security examples that show my intuition is correct.