Stdcall and cdecl

There are (among others) two types of calling conventions - stdcall and cdecl . I have few questions for them:

  • When the cdecl function is called, how does the caller know if he should free the stack? At the call site, is the caller knowing whether the called function is calling cdecl or stdcall? How it works? How does the caller know if he free the stack or not? Or is it the responsibility of linkers?
  • If a function declared as stdcall calls a function (which has a calling convention like cdecl), or vice versa, is this inappropriate?
  • In general, can we say which call will be faster - cdecl or stdcall?
+79
c ++ stdcall cdecl
Aug 04 '10 at 9:51 on
source share
9 answers

Raymond Chen gives a good overview of what __stdcall and __cdecl do .

(1) The caller โ€œknowsโ€ to clear the stack after calling the function, because the compiler knows the calling convention of this function and generates the necessary code.

 void __stdcall StdcallFunc() {} void __cdecl CdeclFunc() { // The compiler knows that StdcallFunc() uses the __stdcall // convention at this point, so it generates the proper binary // for stack cleanup. StdcallFunc(); } 

Possible inconsistency of the calling agreement , for example:

 LRESULT MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); // ... // Compiler usually complains but there this cast here... windowClass.lpfnWndProc = reinterpret_cast<WNDPROC>(&MyWndProc); 

Thus, many code examples are erroneous, not even funny. It should be like this:

 // CALLBACK is #define'd as __stdcall LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg WPARAM wParam, LPARAM lParam); // ... windowClass.lpfnWndProc = &MyWndProc; 

However, assuming that the programmer is not ignoring compiler errors, the compiler will generate the code necessary to properly clear the stack, since it will know the conventions for calling related functions.

(2) Both methods should work. In fact, this happens quite often, at least in code that interacts with the Windows API, because __cdecl is used by default for C and C ++ programs according to the Visual C ++ compiler and WinAPI functions use the __stdcall .

(3) There should be no real difference in performance between the two.

+72
Aug 04 '10 at 9:58
source share

In CDECL, arguments are pushed onto the stack in the reverse order, the caller clears the stack and the result is returned through the processor registry (I will call it โ€œregister Aโ€ later). STDCALL has one difference: the caller does not clear the stack, but the caller.

You ask which one is faster. No one. Nobody. You must use the calling convention as long as possible. Change the agreement only if there is no way out when using external libraries that require the use of a specific agreement.

In addition, there are other conventions that the compiler can choose by default, i.e. Visual C ++ compiler uses FASTCALL, which is theoretically faster due to the wider use of processor registers.

Usually you should give the correct signature of the calling convention for callback functions passed to some external library, i.e. qsort callback from C library should be CDECL (if the compiler uses a different convention by default, then we should mark the callback as CDECL ) or various WinAPI callbacks must be STDCALL (all WinAPI is STDCALL).

Another common case may be when you save pointers to some external functions, i.e. To create a pointer to a WinAPI function, its type definition must be marked as STDCALL.

And below is an example showing how the compiler does it:

 /* 1. calling function in C++ */ i = Function(x, y, z); /* 2. function body in C++ */ int Function(int a, int b, int c) { return a + b + c; } 

Cdecl:

 /* 1. calling CDECL 'Function' in pseudo-assembler (similar to what the compiler outputs) */ push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x' call (jump to function body, after function is finished it will jump back here, the address where to jump back is in registers) move contents of register A to 'i' variable pop all from the stack that we have pushed (copy of x, y and z) /* 2. CDECL 'Function' body in pseudo-assembler */ /* Now copies of 'a', 'b' and 'c' variables are pushed onto the stack */ copy 'a' (from stack) to register A copy 'b' (from stack) to register B add A and B, store result in A copy 'c' (from stack) to register B add A and B, store result in A jump back to caller code (a, b and c still on the stack, the result is in register A) 

STDCALL:

 /* 1. calling STDCALL in pseudo-assembler (similar to what the compiler outputs) */ push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x' call move contents of register A to 'i' variable /* 2. STDCALL 'Function' body in pseaudo-assembler */ pop 'a' from stack to register A pop 'b' from stack to register B add A and B, store result in A pop 'c' from stack to register B add A and B, store result in A jump back to caller code (a, b and c are no more on the stack, result in register A) 
+41
Aug 04 '10 at 10:28
source share

I noticed a message saying it doesn't matter if you call __stdcall from __cdecl or vice versa. It does.

Reason: with __cdecl arguments passed to the called functions are removed from the stack by the calling function, in __stdcall arguments are removed from the stack by the called function. If you call the __cdecl function with __stdcall , the stack is not cleared at all, so ultimately, when __cdecl uses the stack-based link for arguments or the return address, the old data in the current stack pointer will be used. If you call the __stdcall function from __cdecl , the __stdcall function clears the arguments on the stack, and then the __cdecl function does it again, possibly removing the calling functions that return information.

The Microsoft Agreement for C is trying to get around this by distorting names. The __cdecl function has an underscore prefix. The __stdcall function with underscore and suffix with the @ sign and the number of bytes to be removed. For example, __cdecl f (x) is bound as _f , __stdcall f(int x) is bound as _f@4 where sizeof(int) is 4 bytes)

If you manage to get around the linker, enjoy the mess of debugging.

+14
Sep 17 '12 at 18:29
source share

I want to improve @ adf88 answer. I feel that the pseudocode for STDCALL does not reflect the way this actually happens. 'a', 'b' and 'c' are not popped from the stack in the function body. Instead, they are unloaded with the ret command ( ret 12 will be used in this case), which returns to the caller in one fell swoop and simultaneously pushes "a", "b" and "c" from the stack.

Here is my version fixed in accordance with my understanding:

STDCALL:

 /* 1. calling STDCALL in pseudo-assembler (similar to what the compiler outputs) */ push on the stack a copy of 'z', then copy of 'y', then copy of 'x' call move contents of register A to 'i' variable 

/* 2. STDCALL 'Function' body in pseaudo-assembler */ copy 'a' (from stack) to register A copy 'b' (from stack) to register B add A and B, store result in A copy 'c' (from stack) to register B add A and B, store result in A jump back to caller code and at the same time pop 'a', 'b' and 'c' off the stack (a, b and c are removed from the stack in this step, result in register A)

+3
Oct 25 '15 at 1:42
source share

It is specified in the function type. When you have a function pointer, it is considered cdecl, unless explicitly stdcall. This means that if you get the stdcall pointer and the cdecl pointer, you cannot exchange them. Two types of functions can call each other without problems, but just get one type if you expect another. As for speed, they play the same role, only in a very slightly different place, it really does not matter.

+2
Aug 04 '10 at 9:59
source share

The caller and the caller must use the same agreement at the invokation point - this is the only way that he could work reliably. Both the caller and the caller follow a predefined protocol โ€” for example, who needs to clear the stack. If the agreements do not match, your program runs in undefined behavior - most likely, it just works spectacularly.

This is required only for each calling site - the calling code itself can be a function with any call.

You should not notice any real performance difference between these conventions. If this becomes a problem, you usually need to make fewer calls - for example, change the algorithm.

+1
Aug 04 '10 at 9:59
source share

It all depends on the compiler and platform. Neither C nor the C ++ standard says anything about convention calls except extern "C" in C ++.

how does the caller know if he should free the stack?

The caller is aware of the calling agreement of the function and processes the call accordingly.

At the call node, the caller knows whether the called function is a cdecl or stdcall function?

Yes.

How it works?

This is part of the function declaration.

How does the caller know if he should free the stack or not?

The caller knows the calling conventions and can act accordingly.

Or is it the responsibility of linkers?

No, the calling convention is part of the function declaration, so the compiler knows everything he needs to know.

If a function declared as stdcall calls a function (which has a calling convention like cdecl) or vice versa, is it inconvenient?

No. Why should it be?

In general, can you say which call will be faster - cdecl or stdcall?

I dont know. Check it out.

+1
Aug 04 '10 at 10:01
source share

a) When the caller is called by the cdecl function, how does the caller know if he should free the stack?

The cdecl modifier is part of the function prototype (or type of function pointer, etc.), so the caller receives information from there and acts accordingly.

b) If a function declared as stdcall calls a function (which has a calling convention like cdecl), or vice versa, would it be inappropriate?

No it's okay.

c) In general, can you say which call will be faster - cdecl or stdcall?

In general, I would refrain from any such statements. The difference matters, for example. when you want to use va_arg functions. Theoretically, it may be that stdcall is faster and generates less code, because it allows you to combine pop-up locals with pop-up arguments, but OTOH with cdecl , you can do the same if you're smart.

Challenging conventions that tend to be faster usually perform some kind of data transfer.

0
Aug 04 '10 at 10:05
source share

Calling conventions have nothing to do with the C / C ++ programming languages, and are more likely specific to how the compiler implements this language. If you use the same compiler consistently, you never have to worry about calls.

However, sometimes we want binary code compiled by different compilers to work correctly. When we do this, we need to define something called the Application Binary Interface (ABI). ABI defines how the compiler converts C / C ++ source code to machine code. This will include calling conventions, name manipulation, and a v-table layout. cdelc and stdcall are two different calling conventions commonly used on x86 platforms.

By placing information about the calling agreement in the header of the source, the compiler will know what code needs to be created to work correctly with this executable file.

-one
Aug 04 '10 at 10:16
source share



All Articles