C++ stdcall的含义和用法是什么?

C++ stdcall的含义和用法是什么?,c++,windows,calling-convention,C++,Windows,Calling Convention,这些天我遇到了很多 MSDN并没有很清楚地解释它的真正含义,何时以及为什么应该使用它,如果有的话 如果有人能提供一个解释,最好是举一两个例子,我将不胜感激。这是一个调用约定,需要正确调用WinAPI函数。调用约定是一组关于如何将参数传递到函数以及如何从函数传递返回值的规则 如果调用方和被调用代码使用不同的约定,则会出现未定义的行为(如) 默认情况下,C++编译器不使用u stdcall-它们使用其他约定。所以,为了从C++调用WiNAPI函数,需要指定它们使用的是y-sddLead——这通常是在

这些天我遇到了很多

MSDN并没有很清楚地解释它的真正含义,何时以及为什么应该使用它,如果有的话


如果有人能提供一个解释,最好是举一两个例子,我将不胜感激。

这是一个调用约定,需要正确调用WinAPI函数。调用约定是一组关于如何将参数传递到函数以及如何从函数传递返回值的规则

如果调用方和被调用代码使用不同的约定,则会出现未定义的行为(如)


默认情况下,C++编译器不使用u stdcall-它们使用其他约定。所以,为了从C++调用WiNAPI函数,需要指定它们使用的是y-sddLead——这通常是在Windows SDK头文件中完成的,并且在声明函数指针时也会这样做。

< P>它指定了函数的调用约定。调用约定是一组如何将参数传递给函数的规则:顺序、每个地址或每个副本、谁清理参数(调用者或被调用者)等等。

C/C++中的所有函数都有特定的调用约定。调用约定的要点是确定如何在调用方和被调用方之间传递数据,以及谁负责清除调用堆栈等操作

windows上最流行的调用约定是

  • \uu stdcall
    ,按相反顺序(从右到左)在堆栈上推送参数
  • \uu cdecl
    ,按相反顺序(从右到左)在堆栈上推送参数
  • \uu clrcall
    ,按顺序(从左到右)将参数加载到CLR表达式堆栈中
  • \uuu fastcall
    ,存储在寄存器中,然后推送到堆栈上
  • \u此调用
    ,被推到堆栈上;此指针存储在ECX中
将此说明符添加到函数声明中基本上会告诉编译器您希望此特定函数具有此特定调用约定

这里记录了调用约定

从这里开始,雷蒙德·陈(Raymond Chen)还做了一个关于各种电话会议历史的长系列(5部分)


    • 不幸的是,对于何时使用和何时不使用,没有简单的答案

      __stdcall意味着函数的参数从第一个推到最后一个。这与uu cdecl和u fastcall相反,前者意味着参数从最后一个推送到第一个,后者将前四个(我认为)参数放在寄存器中,其余参数放在堆栈上


      您只需要知道被调用方期望什么,或者如果您正在编写库,调用方可能期望什么,并确保记录您选择的约定。

      \u stdcall是一种调用约定:一种确定参数如何传递给函数(在堆栈或寄存器中)的方法函数返回后由谁负责清理(调用者或被调用者)

      /* example of __stdcall */
      push arg1 
      push arg2 
      push arg3 
      call function // no stack cleanup - callee does this
      
      陈雷蒙写了一篇文章,还有一篇很好的文章


      在大多数情况下,你不必担心他们。唯一应该使用的情况是,如果调用的库函数使用的不是默认值,否则编译器将生成错误的代码,程序可能会崩溃。

      \u stdcall表示调用约定(有关详细信息,请参阅)。这意味着它指定如何从堆栈中推送和弹出函数参数,以及由谁负责


      __stdcall只是几个调用约定之一,在整个WINAPI中都使用。如果提供函数指针作为某些函数的回调,则必须使用它。通常,您不需要在代码中表示任何特定的调用约定,只需使用编译器的默认值,上述情况除外(提供对第三方代码的回调)。

      简单地说,当您调用函数时,它将加载到堆栈/寄存器中__stdcall是一种约定/方式(首先是右参数,然后是左参数…)。\u decl是另一种用于在堆栈或寄存器上加载函数的约定

      如果使用它们,则指示计算机在链接期间使用该特定方式加载/卸载函数,因此不会出现不匹配/崩溃


      否则,函数被调用方和函数调用方可能使用不同的约定,导致程序崩溃。

      此答案涵盖32位模式。(Windows x64仅使用两种约定:普通约定(如果它有名称,则称为
      \uuuu fastcall
      )和
      \uuuu vectorcall
      ,除了传递类似
      \uuuuu m128i
      的SIMD向量参数外,这两种约定是相同的)


      传统上,C函数调用是由调用方将一些参数推送到堆栈上,调用函数,然后弹出堆栈以清除这些推送的参数

      /* example of __cdecl */
      push arg1
      push arg2
      push arg3
      call function
      add esp,12    ; effectively "pop; pop; pop"
      
      注:上面显示的默认约定称为_cdecl

      另一个最流行的惯例是u stdcall。在其中,参数再次由调用者推送,但堆栈由被调用者清理。它是Win32 API函数的标准约定(由中的WINAPI宏定义),有时也称为“Pascal”调用约定

      /* example of __stdcall */
      push arg1 
      push arg2 
      push arg3 
      call function // no stack cleanup - callee does this
      
      这看起来像是一个次要的技术细节,但是如果调用方和被调用方之间在如何管理堆栈方面存在分歧,那么堆栈将以不太可能恢复的方式被销毁。 由于uu stdcall执行堆栈清理,执行此任务的(非常小的)代码只在一个地方找到,而不是像在u cdecl中一样在每个调用程序中重复。这使得代码非常小,尽管大小影响仅在大型程序中可见

      (优化编译器有时会为从同一函数和
      mov
      arg进行的多个cdecl调用中分配的arg留出空间