The difference between _cdecl and _stdcal and the function of extern “c

Article directory

    • Purpose
    • _stdcal
    • _cdecl
    • result
    • dll extern “c”
    • Replenish

Purpose

1) Understand the difference between _cdecl and _stdcal under Windows, so that you can better use modified functions to make function calls. There are several calling methods, divided into C style and Pascal style. The overall difference.

  • _cdecl (c/c++ default, supports variable parameter functions, but other non-c/c++ languages do not support it)
  • _stdcall (according to the meaning of the word, standard call, does not support variable parameters)

2) dll extern “c function

_stdcal

WINAPI (actually PASCAL, CALLBACK, _stdcall)

 void WINAPI Input( int & amp;m,int & amp;n);

**Features:** Responsible for pushing the stack in the main calling function, popping the parameters in the stack in the called function, and restoring the stack. Therefore, the variable parameter function cannot be implemented, and the parameters are passed from right to left. In addition, the name modification method is to add an underscore (_) before the function, a symbol (@) after the function name, and the @ is followed by the number of bytes occupied by the parameters in the parameter list (decimal), such as: void Input(int & amp;m,int & amp;n), modified to: _Input@8
CALLBACK is used for most api functions and window message processing functions, so before calling, the calling function will push the stack first, and then the api function restores the stack itself.

_cdecl

C-style calls are the default in C and C++ unless otherwise specified.

 void Input( int & amp;m,int & amp;n);

Features: The default function modification _cdecl in C or C++ language calls. The main calling function pushes parameters onto the stack and restores the stack. The order of pushing actual parameters is from right to left, and finally Stack restoration is performed by the calling function. Since the main calling function manages the stack, variable parameter functions can be implemented. In addition, the name modification method is to add an underscore (_) before the function.

Prove that what is actually passed by reference is the address of the variable (similar to a pointer)

Results

__stdcall and _cdecl

The __stdcall calling convention is used to call Win32 API functions.
 The callee cleans the stack, so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype. The __stdcall modifier is Microsoft-specific.

The __stdcall calling convention is used to call Win32 API functions. The callee clears the stack, so the compiler sets the variadic function to __cdecl. Functions using this calling convention require a function prototype. The __stdcall modifier is Microsoft specific.

Detailed explanation of _cdecl and _stdcall

__stdcall

dll extern “c”

There are two ways to use dynamic link libraries, one is to call explicitly. One is implicit calling.

  • Explicit call: Use LoadLibrary to load the dynamic link library, and use GetProcAddress to obtain the address of a function.
  • Implicit call: You can use #pragma comment(lib, “XX.lib”), or you can directly add XX.lib to the project.

Dll writing needs to solve function renaming – Name-Mangling. There are two solutions. One is to solve it directly in the code using extent “c”, _declspec(dllexport), #pragma comment(linker, “/export:[Exports Name]=[Mangling Name]”), and the other is Def file is used.

Because the renaming rules of C and C++ are different. This kind of renaming is called “Name-Mangling” (name modification or name adaptation, identifier renaming).

It is said that the C++ standard does not stipulate the Name-Mangling scheme, so different compilers use different ones. For example: Borland C++ is different from Microsoft C++, and different versions of compilers may have their Name-Mangling scheme. Mangling rules are also different. In this case, the object file .obj compiled by different compilers is not universal, because the same function using different Name-Mangling will have different names in the obj file. If the function renaming rules in the DLL are inconsistent with the renaming rules adopted by the users of the DLL, the function will not be found.

The C standard stipulates the specifications of the C language Name-Mangling (Lin Rui’s book said this). This allows any compiler that supports the C language to compile obj files that can be shared and linked into executable files. This is a standard. If the DLL and its users adopt this convention, errors caused by inconsistent function renaming rules can be solved.

In addition to the differences between C++ and C and compiler differences that affect symbol names, we also need to consider Name Mangling caused by calling conventions. For example, the calling method of extern “c” __stdcall will add symbols indicating parameters to the original function name, while extern “c” __cdecl will not append additional symbols.

Functions in the dll are indexed by function name or function number when called. This means that the dll file generated by a certain compiler’s C++ Name-Mangling method may not be universal. Because their function names are renamed differently. In order to make the dll more versatile, C’s Name-Mangling method is often used, that is, each exported function is declared as extern “C”, and the _stdcall calling convention is used, and then the exported function needs to be renamed. In order to export the unmodified function name.

Note that the role of extern “C” is to solve the problem of function symbol names, which is a rule that both the manufacturer of the dynamic link library and the user of the dynamic link library need to abide by.

The explicit loading of the dynamic link library is to obtain the function address through the GetProcAddress function based on the dynamic link library handle and function name. Because GetProcAddress is only related to the operating system and may operate dlls generated by various compilers. The function name in its parameters is the original function name without any modification, so generally it is necessary to ensure that the functions in the dll’ name is the original function name. Two steps: First, if the exported function uses extern “C” _cdecl, then there is no need to rename it. At this time, the name in the dll is the original name; if extern “C” _stdcall is used, then the name in the dll The function name has been modified and needs to be renamed. 2. There are two ways to rename, either use a *.def file and modify it outside the file, or use #pragma to alias the function in the code.

  • The functions of _declspec(dllexport) and _declspec(dllimport)

    _declspec has other uses. Here we only discuss the uses related to dll. Just like the keywords in brackets, export and import. _declspec(dllexport) is used on dll to indicate that this is an exported function. And _declspec(dllimport) is used in the program that calls the dll to indicate that this is a function imported from the dll.

     Because the function must be specified in the dll for export, _declspec(dllexport) is necessary. But there is another way, you can use a def file to indicate which functions are used for export, and there is also a function number in the def file.
    

Using _declspec(dllimport) is not required, but it is recommended. Because if _declspec(dllimport) is not used to indicate that the function is imported from dll, then the compiler will not know where the function is. There will be a call XX instruction in the generated exe. This XX is a constant address, XX The address is a jmp dword ptr[XXXX] instruction, which jumps to the function body of the function. Obviously, this adds an intermediate jump for no reason. If _declspec(dllimport) is used to explain, then call dword ptr[XXX] will be generated directly, so that there will be no redundant jumps.

  • __stdcall impact

This is a way of calling a function. By default, VC uses the __cdecl function calling method. If the generated dll will only be used by C/C++ programs, then there is no need to define it as the __stdcall calling method. If it is used for Win32 assembly (or other __stdcall calling method), then you can use __stdcall. This may not be very important, because you can set the function calling rules yourself when calling the function. Like VC, you can set the function calling method, so you can easily use the dll generated by win32 assembly. However, the calling convention of __stdcall is Name-Mangling, so I think it is easier to use the VC default calling convention. However, if you want both the __stdcall calling convention and the function name not to be modified, you can use a *.def file or provide an alias for the function using #pragma in the code (this method requires knowing that the modified function name is What).

·extern “C” __declspec(dllexport) bool __stdcall cswuyg();
·extern “C”__declspec(dllimport) bool __stdcall cswuyg();
·#pragma comment(linker, "/export:cswuyg=_cswuyg@0")

Conclusion:
In general, when writing a DLL, write a header file, which declares the function’s Nameming method and calling convention (mainly for implicit calling). Write another
.def file and rename the function (mainly for explicit calling). Provide
.DLL*.lib*.h to users of dll, so that whether it is an implicit call or an explicit call, it can be done conveniently. **

I am very grateful to this blogger for writing about the functions of extern “C” and __stdcall in DLL writing. He wrote it in detail and clearly.

Supplement

1) Common occasions for calling protocols

  • __stdcall: Windows API default function calling protocol.
  • __cdecl: C/C++ default function calling protocol.
  • __fastcall: Suitable for occasions with high performance requirements.

2) How to push function parameters onto the stack

  • __stdcall: Function parameters are pushed onto the stack from right to left.
  • __cdecl: Function parameters are pushed onto the stack from right to left.
  • __fastcall: Parameters no larger than 4 bytes starting from the left are put into the ECX and EDX registers of the CPU, and the remaining parameters are pushed onto the stack from right to left.
    Question 1: __fastcall puts parameters no larger than 4 bytes in the register, so it has high performance and is suitable for occasions that require high performance.

3) How to clear data in the stack

  • __stdcall: After the function call ends, the called function clears the stack data.
  • __cdecl: After the function call ends, the function caller clears the data in the stack.
  • __fastcall: After the function call ends, the called function clears the stack data.
    Problem 1: The stack structures set by different compilers are different. It is not feasible for the function caller to clear the data in the stack across development platforms.
    Question 2: The parameters of some functions are variable, such as the printf function. Such functions can only clear the stack data by the function caller.
    Problem 3: When the caller clears the data in the stack, each call contains code to clear the data in the stack, so the executable file is larger.

4) C language compiler function name modification rules

  • __stdcall: After compilation, the function name is modified to “_functionname@number”.
  • __cdecl: After compilation, the function name is modified to “_functionname”.
  • __fastcall: After compilation, the function name is modified to “@functionname@nmuber”.
    Note: “functionname” is the function name, and “number” is the number of parameter bytes.
    Note: If different function calling protocols are used in function implementation and function definition, function calling cannot be implemented.

5) C++ language compiler function name modification rules

  • __stdcall: After compilation, the function name is modified to “?functionname@@YG******@Z”.
  • __cdecl: After compilation, the function name is modified to “?functionname@@YA******@Z”.
  • __fastcall: After compilation, the function name is modified to “?functionname@@YI******@Z”.
    Note: “******” is the function return value type and parameter type table.
    Note: If different function calling protocols are used in function implementation and function definition, function calling cannot be implemented.

6) If there is no special processing between C language and C++ language, it is impossible to call each other’s functions.