C++ __stdcall 的含义和用法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1306414/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the meaning and usage of __stdcall?
提问by vehomzzz
I've come across __stdcall
a lot these days.
__stdcall
这几天我遇到了很多。
MSDN doesn't explain very clearly what it really means, when and why should it be used, if at all.
MSDN 并没有很清楚地解释它的真正含义,何时以及为什么应该使用它,如果有的话。
I would appreciate if someone would provide an explanation, preferably with an example or two.
如果有人能提供解释,最好是一两个例子,我将不胜感激。
采纳答案by JaredPar
All functions in C/C++ have a particular calling convention. The point of a calling convention is to establish how data is passed between the caller and callee and who is responsible for operations such as cleaning out the call stack.
C/C++ 中的所有函数都有特定的调用约定。调用约定的重点是确定如何在调用方和被调用方之间传递数据,以及谁负责清理调用堆栈等操作。
The most popular calling conventions on windows are
Windows 上最流行的调用约定是
__stdcall
, Pushes parameters on the stack, in reverse order (right to left)__cdecl
, Pushes parameters on the stack, in reverse order (right to left)__clrcall
, Load parameters onto CLR expression stack in order (left to right).__fastcall
, Stored in registers, then pushed on stack__thiscall
, Pushed on stack; this pointer stored in ECX
__stdcall
, 以相反的顺序(从右到左)将参数压入堆栈__cdecl
, 以相反的顺序(从右到左)将参数压入堆栈__clrcall
, 按顺序(从左到右)将参数加载到 CLR 表达式堆栈中。__fastcall
, 存储在寄存器中,然后压入堆栈__thiscall
, 压入堆栈;此指针存储在 ECX 中
Adding this specifier to the function declaration essentially tells the compiler that you want this particular function to have this particular calling convention.
将此说明符添加到函数声明实质上是告诉编译器您希望此特定函数具有此特定调用约定。
The calling conventions are documented here
调用约定记录在此处
Raymond Chen also did a long series on the history of the various calling conventions (5 parts) starting here.
Raymond Chen 还从这里开始就各种调用约定的历史(5 部分)做了一个长系列。
回答by edge
Traditionally, C function calls are made with the caller pushing some parameters onto the stack, calling the function, and then popping the stack to clean up those pushed arguments.
传统上,C 函数调用是通过调用者将一些参数推入堆栈,调用函数,然后弹出堆栈以清除这些推入的参数来进行的。
/* example of __cdecl */
push arg1
push arg2
push arg3
call function
add sp,12 // effectively "pop; pop; pop"
Note: The default convention — shown above — is known as __cdecl.
注意:默认约定——如上所示——被称为 __cdecl。
The other most popular convention is __stdcall. In it the parameters are again pushed by the caller, but the stack is cleaned up by the callee. It is the standard convention for Win32 API functions (as defined by the WINAPI macro in ), and it's also sometimes called the "Pascal" calling convention.
另一个最流行的约定是 __stdcall。其中参数再次由调用者压入,但堆栈由被调用者清理。它是 Win32 API 函数的标准约定(由 中的 WINAPI 宏定义),有时也称为“Pascal”调用约定。
/* example of __stdcall */
push arg1
push arg2
push arg3
call function // no stack cleanup - callee does this
This looks like a minor technical detail, but if there is a disagreement on how the stack is managed between the caller and the callee, the stack will be destroyed in a way that is unlikely to be recovered. Since __stdcall does stack cleanup, the (very tiny) code to perform this task is found in only one place, rather than being duplicated in every caller as it is in __cdecl. This makes the code very slightly smaller, though the size impact is only visible in large programs.
这看起来像是一个次要的技术细节,但是如果调用者和被调用者之间在如何管理堆栈方面存在分歧,那么堆栈将以一种不太可能恢复的方式被销毁。由于 __stdcall 进行堆栈清理,执行此任务的(非常小的)代码只能在一个地方找到,而不是像在 __cdecl 中那样在每个调用者中重复。这使得代码非常小,尽管大小影响仅在大型程序中可见。
Variadic functions like printf() are almost impossible to get right with __stdcall, because only the caller really knows how many arguments were passed in order to clean them up. The callee can make some good guesses (say, by looking at a format string), but the stack cleanup would have to be determined by the actual logic of the function, not the calling-convention mechanism itself. Hence only __cdecl supports variadic functions so that the caller can do the cleanup.
像 printf() 这样的可变参数函数几乎不可能用 __stdcall 得到正确的结果,因为只有调用者才真正知道传递了多少参数以便清理它们。被调用者可以做出一些很好的猜测(例如,通过查看格式字符串),但堆栈清理必须由函数的实际逻辑决定,而不是调用约定机制本身。因此只有 __cdecl 支持可变参数函数,以便调用者可以进行清理。
Linker symbol name decorations: As mentioned in a bullet point above, calling a function with the "wrong" convention can be disastrous, so Microsoft has a mechanism to avoid this from happening. It works well, though it can be maddening if one does not know what the reasons are. They have chosen to resolve this by encoding the calling convention into the low-level function names with extra characters (which are often called "decorations"), and these are treated as unrelated names by the linker. The default calling convention is __cdecl, but each one can be requested explicitly with the /G? parameter to the compiler.
链接器符号名称修饰:如上面的要点中所述,调用具有“错误”约定的函数可能是灾难性的,因此 Microsoft 有一种机制可以避免这种情况发生。它运作良好,但如果不知道原因是什么可能会令人抓狂。他们选择通过将调用约定编码为带有额外字符(通常称为“装饰”)的低级函数名称来解决此问题,链接器将这些名称视为不相关的名称。默认调用约定是 __cdecl,但可以使用 /G? 编译器的参数。
__cdecl (cl /Gd ...)
__cdecl (cl /Gd ...)
All function names of this type are prefixed with an underscore, and the number of parameters does not really matter because the caller is responsible for stack setup and stack cleanup. It is possible for a caller and callee to be confused over the number of parameters actually passed, but at least the stack discipline is maintained properly.
这种类型的所有函数名称都以下划线为前缀,参数的数量并不重要,因为调用者负责堆栈设置和堆栈清理。调用者和被调用者可能会对实际传递的参数数量感到困惑,但至少堆栈规则得到了适当的维护。
__stdcall (cl /Gz ...)
__stdcall (cl /Gz ...)
These function names are prefixed with an underscore and appended with @ plus the number of bytes of parameters passed. By this mechanism, it's not possible to call a function with the "wrong" type, or even with the wrong number of parameters.
这些函数名称以下划线为前缀,并附加 @ 加上传递的参数的字节数。通过这种机制,不可能调用具有“错误”类型的函数,甚至是错误数量的参数。
__fastcall (cl /Gr ...)
__fastcall (cl /Gr ...)
These function names start with an @ sign and are suffixed with the @parameter count, much like __stdcall.
这些函数名称以@ 符号开头,并以@parameter 计数为后缀,很像__stdcall。
Examples:
例子:
Declaration -----------------------> decorated name
void __cdecl foo(void); -----------------------> _foo
void __cdecl foo(int a); -----------------------> _foo
void __cdecl foo(int a, int b); -----------------------> _foo
void __stdcall foo(void); -----------------------> _foo@0
void __stdcall foo(int a); -----------------------> _foo@4
void __stdcall foo(int a, int b); -----------------------> _foo@8
void __fastcall foo(void); -----------------------> @foo@0
void __fastcall foo(int a); -----------------------> @foo@4
void __fastcall foo(int a, int b); -----------------------> @foo@8
回答by Nick Meyer
__stdcall is a calling convention: a way of determining how parameters are passed to a function (on the stack or in registers) and who is responsible for cleaning up after the function returns (the caller or the callee).
__stdcall 是一种调用约定:一种确定参数如何传递给函数(在堆栈上或在寄存器中)以及函数返回后谁负责清理(调用者或被调用者)的方法。
Raymond Chen wrote a blog about the major x86 calling conventions, and there's a nice CodeProject articletoo.
Raymond Chen 写了一篇关于主要 x86 调用约定的博客,还有一篇很好的CodeProject 文章。
For the most part, you shouldn't have to worry about them. The only case in which you should is if you're calling a library function that uses something other than the default -- otherwise the compiler will generate the wrong code and your program will probably crash.
大多数情况下,您不必担心它们。唯一应该出现的情况是,如果您调用的库函数使用的不是默认值——否则编译器将生成错误的代码,您的程序可能会崩溃。
回答by Jonathan
Unfortunately, there is no easy answer for when to use it and when not.
不幸的是,对于何时使用它,何时不使用它没有简单的答案。
__stdcall means that the arguments to a function are pushed onto the stack from the first to the last. This is as opposed to __cdecl, which means that the arguments are pushed from last to first, and __fastcall, which places the first four (I think) arguments in registers, and the rest go on the stack.
__stdcall 意味着函数的参数从第一个到最后一个压入堆栈。这与 __cdecl 不同,这意味着参数从最后推到第一个,而 __fastcall 将前四个(我认为)参数放在寄存器中,其余的放在堆栈中。
You just need to know what the callee expects, or if you are writing a library, what your callers are likely expect, and make sure you document your chosen convention.
你只需要知道被调用者期望什么,或者如果你正在编写一个库,你的调用者可能期望什么,并确保你记录你选择的约定。
回答by sbi
It specifies a calling convention for a function. A calling convention is a set of rules how parameters are passed to a function: in which order, per address or per copy, who is to clean up the parameters (caller or callee) etc.
它指定函数的调用约定。调用约定是一组如何将参数传递给函数的规则:按顺序、每个地址或每个副本、谁来清理参数(调用者或被调用者)等。
回答by gimpf
__stdcall denotes a calling convention (see this PDFfor some details). This means it specifies how function arguments are pushed and popped from the stack, and who is responsible.
__stdcall 表示调用约定(有关详细信息,请参阅此 PDF)。这意味着它指定了函数参数如何从堆栈中压入和弹出,以及谁负责。
__stdcall is just one of several calling conventions, and is used throughout the WINAPI. You must use it if you provide function pointers as callbacks for some of those functions. In general, you do not need to denote any specific calling convention in your code, but just use the compiler's default, except for the case noted above (providing callbacks to 3rd party code).
__stdcall 只是几种调用约定之一,并在整个 WINAPI 中使用。如果您提供函数指针作为其中一些函数的回调,则必须使用它。通常,您不需要在代码中指定任何特定的调用约定,而只需使用编译器的默认值,除了上面提到的情况(提供对 3rd 方代码的回调)。
回答by dev ray
simply put when you call function, it gets loaded in stack/register. __stdcall is one convention/way(right argument first, then left argument ...), __decl is another convention that are used to load the function on the stack or registers.
简单地说,当您调用函数时,它会加载到堆栈/寄存器中。__stdcall 是一种约定/方式(首先是右参数,然后是左参数......),__decl 是另一种用于在堆栈或寄存器上加载函数的约定。
If you use them you instruct the computer to use that specific way to load/unload the function during linking and hence you would not get a mismatch/crash.
如果您使用它们,您会指示计算机在链接期间使用该特定方式加载/卸载该功能,因此您不会遇到不匹配/崩溃的情况。
Otherwise the function-callee and function-caller might use different conventions causing program to crash.
否则函数被调用者和函数调用者可能会使用不同的约定导致程序崩溃。
回答by Anand
__stdcallis the calling convention used for the function. This tells the compiler the rules that apply for setting up the stack, pushing arguments and getting a return value. There are a number of other calling conventions like __cdecl, __thiscall, __fastcalland __naked.
__stdcall是用于函数的调用约定。这告诉编译器适用于设置堆栈、推送参数和获取返回值的规则。还有许多其他调用约定,如__cdecl、__thiscall、__fastcall和__naked。
__stdcallis the standard calling convention for Win32 system calls.
__stdcall是 Win32 系统调用的标准调用约定。
More details can be found on Wikipedia.
可以在Wikipedia上找到更多详细信息。
回答by sharptooth
That's a calling convention that WinAPI functions need to be called properly. A calling convention is a set of rules on how the parameters are passed into the function and how the return value is passed from the function.
这是 WinAPI 函数需要正确调用的调用约定。调用约定是一组关于如何将参数传递给函数以及如何从函数传递返回值的规则。
If the caller and the called code use different conventions you run into undefined behaviour (like such a strange-looking crash).
如果调用者和被调用的代码使用不同的约定,您会遇到未定义的行为(例如看起来很奇怪的 crash)。
C++ compilers don't use __stdcall by default - they use other conventions. So in order to call WinAPI functions from C++ you need to specify that they use __stdcall - this is usually done in Windoes SDK header files and you also do it when declaring function pointers.
C++ 编译器默认不使用 __stdcall - 它们使用其他约定。因此,为了从 C++ 调用 WinAPI 函数,您需要指定它们使用 __stdcall - 这通常在 Windoes SDK 头文件中完成,并且您在声明函数指针时也会这样做。