Calling a function that can be either cdecl or stdcall
It can be done following way:
mov esi, esp
push arg3
push arg2
push arg1
call [SomeExternalProc]
mov esp, esi ; now the stack is always properly cleaned
The external procedure will preserve esi. Or you can use any other register preserved by the external procedure or even memory variable - local or global.
Good, the order of the arguments is the same for CDECL and STDCALL - in reverse order. (Left-most arg at the lowest address.) So they're compatible except for where ESP points on return. Both conventions agree on which registers are call-preserved vs. call-clobbered.
Confused about __cdecl and __stdcall calling convention
"Clean up the stack" actually means "adjust the stack pointer so that it matches the changes done to stack pointer when arguments and return address were pushed onto stack". In your example it would be irrelevant who cleans up the stack - you would get the same undefined behavior, because you try to access a stack-allocated variable that is overwritten by a series of later pushes of arguments needed to perform printf()
.
If you only write code in C or C++ you should not care of the exact details like "who cleans up the stack" - those details are critical only when writing in assembly. In C and C++ just be sure to mark function pointers with right calling conventions before calling a function through those pointers and the compiler will do the rest.
__cdecl or __stdcall on Windows?
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.
Which one to choose, stdcall or cdecl?
As a general rule, functions exported from Microsoft's CRT implementations use the cdecl
calling convention.
Related Topics
C++, Usleep() Is Obsolete, Workarounds for Windows/Mingw
Static and Extern Global Variables in C and C++
How to Use Visual Studio 2010's C++ Compiler with Visual Studio 2008's C++ Runtime Library
Why Would Someone Use #Define to Define Constants
How-To Initialize 'Const Std::Vector<T>' Like a C Array
Where Is '%P' Useful with Printf
Are Destructors Called After a Throw in C++
Double Free or Corruption After Queue::Push
Std::Vector to Boost::Python::List
C++: Argument Passing "Passed by Reference"
What's This C++ Syntax That Puts a Brace-Surrounded Block Where an Expression Is Expected
Calling Virtual Function from Destructor
Variable Assignment in an "If" Condition