__stdcall 的 advantage/use 是什么?
What is advantage/use of __stdcall?
在发布这个问题之前,我已经阅读了一些文档,SO answers 并观看了一些视频来理解 __stdcall
。到目前为止,我已经理解这是一个调用约定,并指定在堆栈上从右向左压入参数。这也表示何时清除堆栈。
但是我仍然无法理解我应该使用__stdcall
的优点和情况。
我遇到了以下代码,它在 OPC-UA 客户端关闭时调用。我看到这应该遵循 __stdcall
调用约定,但为什么呢?如果未为以下方法指定 __stdcall
,可能会发生什么情况?
OpcUa_StatusCode __stdcall opcua_client::onShutdownMessage(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_String strShutdownMessage, void* extraParam)
{
opcua_client* pOpcClient = NULL;
if (extraParam)
{
try
{
pOpcClient = qobject_cast <opcua_client*>((QObject*)extraParam);
throw(55);
}
catch (int exeption)
{
}
}
OpcUa_StatusCode uStatus = OpcUa_Good;
QString strShutDownMsg = QString::fromUtf8(OpcUa_String_GetRawString(&strShutdownMessage));
bool bOK;
OpcUa_StatusCode uClientLibStatus = uClientLibStatus = strShutDownMsg.toUInt(&bOK, 16);
if (pOpcClient)
{
if (uClientLibStatus > 0)
pOpcClient->process_onShutdownMessage(uClientLibStatus);
}
else
{
return uStatus;
}
return uStatus;
}
opcua_client::onShutdownMessage
看起来像回调函数。也就是说,一个函数被发送到某个 API 稍后调用该函数。回调函数必须具有 API 所期望的调用约定,在本例中为 __stdcall
。由于 __stdcall
是 Win32 平台上的默认调用约定,因此在为 Win32 构建时不必指定。
调用者必须遵守函数调用约定。如果它在那里,那么一定有这个函数的某个调用者假定它必须以特定的方式被调用。当您有回调函数时,这通常很重要。
除此之外,与 cdecl
相比,stdcall
旨在通过将参数清理代码从调用者移动到被调用者(可以在 x86 上使用 ret imm16
)来减少代码大小.
这是针对 IA-32 架构的旧微优化。 MSVC 在其他架构上也接受 __stdcall
关键字,但它 doesn't do anything there.
检查这个例子:
__declspec(noinline) int sum(int a, int b)
{
return a + b;
}
int add1(int a)
{
return sum(a, 1);
}
MSVC 将其编译为:
int sum(int,int) PROC
mov eax, DWORD PTR _a$[esp-4]
add eax, DWORD PTR _b$[esp-4]
ret 0 ; this instruction will be replaced
int sum(int,int) ENDP
int add1(int) PROC
push 1
push DWORD PTR _a$[esp]
call int sum(int,int)
add esp, 8 ; this instruction will be removed
ret 0
int add1(int) ENDP
和 stdcall
的版本:
__declspec(noinline) int __stdcall sum(int a, int b)
{
return a + b;
}
int add1(int a)
{
return sum(a, 1);
}
编译为:
int sum(int,int) PROC
mov eax, DWORD PTR _a$[esp-4]
add eax, DWORD PTR _b$[esp-4]
ret 8 ; 0 argument replaced with 8
int sum(int,int) ENDP
int add1(int) PROC
push 1
push DWORD PTR _a$[esp]
call int sum(int,int)
ret 0 ; extra instruction is missing here
int add1(int) ENDP
注意第二个示例如何使用 ret 8
并且调用者没有额外的 add esp, 8
.
在发布这个问题之前,我已经阅读了一些文档,SO answers 并观看了一些视频来理解 __stdcall
。到目前为止,我已经理解这是一个调用约定,并指定在堆栈上从右向左压入参数。这也表示何时清除堆栈。
但是我仍然无法理解我应该使用__stdcall
的优点和情况。
我遇到了以下代码,它在 OPC-UA 客户端关闭时调用。我看到这应该遵循 __stdcall
调用约定,但为什么呢?如果未为以下方法指定 __stdcall
,可能会发生什么情况?
OpcUa_StatusCode __stdcall opcua_client::onShutdownMessage(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_String strShutdownMessage, void* extraParam)
{
opcua_client* pOpcClient = NULL;
if (extraParam)
{
try
{
pOpcClient = qobject_cast <opcua_client*>((QObject*)extraParam);
throw(55);
}
catch (int exeption)
{
}
}
OpcUa_StatusCode uStatus = OpcUa_Good;
QString strShutDownMsg = QString::fromUtf8(OpcUa_String_GetRawString(&strShutdownMessage));
bool bOK;
OpcUa_StatusCode uClientLibStatus = uClientLibStatus = strShutDownMsg.toUInt(&bOK, 16);
if (pOpcClient)
{
if (uClientLibStatus > 0)
pOpcClient->process_onShutdownMessage(uClientLibStatus);
}
else
{
return uStatus;
}
return uStatus;
}
opcua_client::onShutdownMessage
看起来像回调函数。也就是说,一个函数被发送到某个 API 稍后调用该函数。回调函数必须具有 API 所期望的调用约定,在本例中为 __stdcall
。由于 __stdcall
是 Win32 平台上的默认调用约定,因此在为 Win32 构建时不必指定。
调用者必须遵守函数调用约定。如果它在那里,那么一定有这个函数的某个调用者假定它必须以特定的方式被调用。当您有回调函数时,这通常很重要。
除此之外,与 cdecl
相比,stdcall
旨在通过将参数清理代码从调用者移动到被调用者(可以在 x86 上使用 ret imm16
)来减少代码大小.
这是针对 IA-32 架构的旧微优化。 MSVC 在其他架构上也接受 __stdcall
关键字,但它 doesn't do anything there.
检查这个例子:
__declspec(noinline) int sum(int a, int b)
{
return a + b;
}
int add1(int a)
{
return sum(a, 1);
}
MSVC 将其编译为:
int sum(int,int) PROC
mov eax, DWORD PTR _a$[esp-4]
add eax, DWORD PTR _b$[esp-4]
ret 0 ; this instruction will be replaced
int sum(int,int) ENDP
int add1(int) PROC
push 1
push DWORD PTR _a$[esp]
call int sum(int,int)
add esp, 8 ; this instruction will be removed
ret 0
int add1(int) ENDP
和 stdcall
的版本:
__declspec(noinline) int __stdcall sum(int a, int b)
{
return a + b;
}
int add1(int a)
{
return sum(a, 1);
}
编译为:
int sum(int,int) PROC
mov eax, DWORD PTR _a$[esp-4]
add eax, DWORD PTR _b$[esp-4]
ret 8 ; 0 argument replaced with 8
int sum(int,int) ENDP
int add1(int) PROC
push 1
push DWORD PTR _a$[esp]
call int sum(int,int)
ret 0 ; extra instruction is missing here
int add1(int) ENDP
注意第二个示例如何使用 ret 8
并且调用者没有额外的 add esp, 8
.