The calling conventions used in Visual C++ are documented. The main reason for having this document here is so that I can reference it from elsewhere for the benefit of those who do not have access to the documentation while they are an article which requires knowledge of the conventions. While I’m here though I might as well try to do as good a job as I can at explaining the different conventions.
C++ provides a way of defining a function and a way of calling that function with arguments that soon become second nature to use. When the function and calls to it are compiled there are quite a few different ways in which the process of getting those arguments to where the function’s code can “see” them before executing the code can be performed. There are pros and cons to each, and some compilers have extensions which allow you do choose which is used.
Most of the time there is no need to do anything other than use the defaults or, in a few cases where you are required to match a binary specification, do what you are told. But you might want to make use of this in some cases, or simply understand what it is going on with calling conventions used by code you interface with.
The C++ Standard [ISO/IEC 14882] has very little to say about calling conventions. It has a bit to say about language linkage and linkage specifications, and it notes that a particular language linkage may be associated with a particular calling convention. Everything else related to calling conventions is implementation specific. Everything else in this article is Microsoft Visual C++ specific, indeed VC++6 or VC++.NET specific (some other calling conventions that have since been obsoleted are not mentioned here, and what is detailed here may change with later versions of VC++).
Since Visual C++ gives you the same ability to alter the calling conventions used with C code as with C++ code the effects of this are slight.
A few features of the thiscall convention and the choice of the __cdecl convention as the default convention follow naturally from how C++ is defined outside of a purely Microsoft-orientated view of the language. These shall be noted as we come to them.
Overloading of functions is resolved at compile time, which means that by to
int doubleIt(int); and
doubleIt(float) are completely different functions. As a means to
implement this the function names are internally “decorated”
with extra characters that describe the parameters, namespace and/or class
membership and return type of the function.
This mechanism is compatible with C, a C function or a function declared to have C linkage is not decorated in this manner. This obviously doesn’t allow you to overload a function with C linkage, and also means that namespaces are ignored and you can have only one function with C linkage with any given name, even if they are declared in different namespaces. Notably these rules are also imposed on functions with C linkage by the Standard Standard [ISO/IEC 14882].
Because otherwise identical functions with different calling conventions would not be compatible it is also necessary to ensure that the linker does not consider two such functions to be one and the same. If they occur in the same compilation unit (.cpp file) then it’s a simple bug for the compiler to catch. However to if a function is declared and used in one compilation unit, and defined in another compilation unit (or an already compiled library) with a different calling convention it isn’t as clear an error. To prevent such a case from being linked both C and C++ functions will be decorated so as to identify the calling convention used. This means that the linker will consider the two uses of the function name to differ, and compilation will stop with a linker error.
You’ve no doubt seen these decorated names when debugging or dealing with linker errors. It can be particularly common when two pieces of code use the same header file, but differ in the default calling convention set. Apart from that name decoration impacts on calling conventions only in so far as different calling conventions means different name decoration.
There is one rule in both the standard and the way Visual C++ works that overrides everything mentioned here, the “as-if” rule. Formally:
The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine. This International Standard places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.
This is an amazingly long-winded, if admirably precise, way of saying that a C++ implementaton can do whatever it wants, as long as the final result is the same as if it followed the standard to the letter. In fairness to the Standard’s authors there is a note explaining the above in more vernacular language.
This isn’t just a concession to the compiler writers, it is the basis of almost all optimisation techniques beyond explicit inlining, and given that much of the design of the Standard Library (in particular the STL) assumes aggressive optimisation on the part of the compiler, compilers are really expected to do this.
The effects on our current topic of discussion is that what I am about to explain may not actually be what happens in the case of a “release” build. Inline function calls don’t involve any of this, and there is the potential that other features may change. This is of little practical importance, but could explain apparant weirdness if you are trying to debug a release build.
Most often when calling conventions become an issue it is with the linking of seperately compiled code. In these cases the scope for optimisation of how the functions are called is limited anyway, so what follows is pretty reliable in debugging such situations.
No matter what the calling convention the following takes place for all function calls:
The __cdecl calling convention is used for functions marked with the
__cdecl keyword, or for unmarked functions when the /Gd
compiler option is set. Since /Gd is the default this is then the default
All arguments are stored on the stack, in right to left order. The code that called the function will have to remove these arguments when the function returns.
Because the calling code cleans the stack this convention can be used with
functions with a variable number of arguments such as
printf(const char*, ...). Since C++ must allow such functions,
as advisable as it is to avoid them when you can, it makes sense that this
is the default calling convention.
The __stdcall calling convention is used for functions marked with the
__stdcall keyword, or for unmarked functions when the /Gz
compiler option is set.
All arguments are stored on the stack, in right to left order. The function itself will clean the stack. This results in slightly smaller executables but cannot be used with functions that take a variable number of arguments.
COM methods, and the Windows API functions are generally __stdcall
functions. The macros
CALLBACK and a
few others used in Windows API declarations expand to
The __fastcall calling convention is used for functions marked with the
__fastcall keyword, or for unmarked functions when the /Gr
compiler option is set (/Gr will not affect main()).
The first two arguments (left to right) which are 32bit or smaller are placed into two registers (currently ECX and EDX, though Microsoft have no commitment to keep using those two with later compilers). All other arguments are stored on the stack, in right to left order. The function itself will clean the stack.
If you attempt to take the address of an argument stored in a register it will be copied to a temporary location.
While reasons this would giva an advantage in speed are clear, with modern machines it tends to be negligible.
The thiscall calling convention cannot be explicitly used. It is used for member functions of classes which are not explicitly set to use a different convention, and which do not take a variable number of arguments (in which case __cdecl is used).
The convention operates much like __stdcall except
this pointer (which is effectively a hidden argument)
is stored in the ECX register. This neatly matches with the C++ rule that
one cannot take the address of the
In the case where a variable number of arguments requires __cdecl to be used the this pointer is considered the left-most argument, and hence pushed on the stack last.