【问题标题】:What is advantage/use of __stdcall?__stdcall 的优点/用途是什么?
【发布时间】:2020-02-07 19:36:41
【问题描述】:

在发布这个问题之前,我已经阅读了一些文档,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;
}

【问题讨论】:

  • 它是 32 位代码中操作系统和 COM 库的标准调用约定。 OPC 很大程度上基于 COM,因此预计会看到很多 stdcall。尽管 onShutdownMessage() 不是标准接口函数之一。也许您使用了一个指示 stdcall 的工具包,一点也不稀奇。

标签: c++ stdcall


【解决方案1】:

调用者必须遵循函数调用约定。如果它在那里,那么一定有这个函数的调用者会假设它必须以特定的方式被调用。当您有回调函数时,这通常很重要。

除此之外,与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

【讨论】:

    【解决方案2】:

    opcua_client::onShutdownMessage 看起来像回调函数。也就是说,一个函数被发送到稍后调用该函数的某个 API。然后,回调函数必须具有 API 预期的调用约定,在本例中为 __stdcall。由于__stdcall是Win32平台上的默认调用约定,因此在为Win32构建时不必指定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-24
      • 2010-09-22
      • 1970-01-01
      • 1970-01-01
      • 2011-10-14
      • 2016-04-16
      • 2010-11-21
      • 1970-01-01
      相关资源
      最近更新 更多