什么是__stdcall?


151

我正在学习Win32编程,其WinMain原型如下所示:

int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show )

我对这个WINAPI标识符的用途感到困惑,发现:

#define WINAPI      __stdcall

这是做什么的?我对在返回类型之后根本没有东西感到困惑。是__stdcall为了什么 返回类型和函数名称之间有什么含义?


2
@AndrewProck在这种情况下,可以进行“虚拟”更改,但是通常可以使用 s来解决愚蠢的(至少适得其反的情况)至少6个字符。
Adi Inbar 2014年

Answers:


170

__stdcall是用于该函数的调用约定。这将告诉编译器适用于设置堆栈,推送参数和获取返回值的规则。

还有一些其他的调用约定,__cdecl__thiscall__fastcall和奇妙命名__declspec(naked)__stdcall是Win32系统调用的标准调用约定。

维基百科涵盖了细节

当您在代码外调用函数(例如OS API)或OS在调用您(WinMain就是这种情况)时,这尤其重要。如果编译器不知道正确的调用约定,则由于堆栈管理不当,您可能会遇到非常奇怪的崩溃。


8
请参阅此问题,以了解非常
严重的

如果我没记错的话,那么这些调用约定将控制编译器如何生成汇编代码。与汇编代码进行接口连接时,请务必注意调用约定,以防止堆栈问题。这里有一张漂亮的桌子,记录着一些约定:msdn.microsoft.com/zh-cn/library/984x0h58.aspx
Nicholas Miller

我们可以说这将禁用某些非法优化吗?
kesari

40

C或C ++本身未定义这些标识符。它们是编译器扩展,代表某些调用约定。这决定了将参数放在何处,以什么顺序放置,被调用函数将在哪里找到返回地址,等等。例如,__fastcall表示函数的参数通过寄存器传递。

维基百科的文章提供了发现有不同的调用约定的概述。


18

到目前为止,答案已经涵盖了所有细节,但是如果您不打算进行汇编,那么您只需知道调用者和被调用者都必须使用相同的调用约定,否则您将得到以下错误:很难找到。


12

我同意到目前为止所有答案都是正确的,但这是原因。Microsoft的C和C ++编译器为应用程序的C和C ++函数内的(预期)函数调用速度提供了各种调用约定。在每种情况下,主叫方和被叫方必须就使用哪种呼叫约定达成一致。现在,Windows本身提供了功能(API),并且这些功能已经被编译,因此,在调用它们时,必须遵循它们。对Windows API的任何调用以及来自Windows API的回调都必须使用__stdcall约定。


3
并且不能将它与_standard_call混淆,因为它是standard-c!有人可能会认为,如果不懂得更好,这将是__stdcall的重点
Johannes Schaub-litb

3
一个小问题:有一些使用__cdecl而不是__stdcall的Windows API-通常使用可变数量的参数,例如wsprintf()。
Michael Burr

你是对的。它的名称看起来像CRT函数,但它是一个API。您是否有机会从C#中P /调用它?
Windows程序员

我还没有测试过,但是pinvoke.net给出了此签名:“静态外部int wsprintf([Out] StringBuilder lpOut,字符串lpFmt,...);”
Michael Burr

我的直觉说,C#编译器不知道在该代码上使用__cdecl约定。
Windows程序员



4

__stdcall用于将函数参数放入堆栈中。功能完成后,它将自动重新分配内存。这用于固定参数。

void __stdcall fnname ( int, int* )
{
    ...
}

int main()
{
    CreateThread ( NULL, 0, fnname, int, int*...... )
}

在这里,fnname具有直接将其压入堆栈的args。


1

直到今天,我再也不需要使用它。这是因为在我的代码中我正在使用多线程,而我正在使用的多线程API是Windows一(_beginthreadex)。

要启动线程:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0);

ExecuteCommand函数必须在方法签名中使用__stdcall关键字,以便beginthreadex调用它:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.