関数呼び出し規則cdecl、stdcall、fastcall

3980 ワード

私たちはコードを書くときに関数を呼び出します.いくつかの関数には複数のパラメータがあります.たとえば、次のようにします.
int test(int a,char b, char* c);

上の関数呼び出し方式はtest(10,‘c’,‘tinus’);では、この関数コンパイラはどのようにしてパラメータがいくつあるかを知っていますか.パラメータのタイプは何ですか.関数呼び出しの場合,呼び出し者はパラメータを順次スタックに圧し,関数を呼び出し,関数が呼び出された後,スタックでデータを取得し,計算を行うからである.関数計算が終了した後、呼び出し者、または関数自体がスタックを修正し、スタックを元の状態に戻します.では、パラメータ伝達でパラメータの数が1つ以上ある場合、パラメータをスタックにどのような順序で押し込み、関数呼び出し後、スタック内のデータは誰が処理を解放しますか?
上記の問題を解決するために、C/C++は関数呼び出し規則によってこれらの問題を説明する.
関数呼び出し規則
は、関数呼び出し者と呼び出された関数体との間のパラメータ伝達、戻り値伝達、スタッククリア、レジスタ使用に関する約定である.一般的な呼び出し規則は次のとおりです.
    stdcall(pascal)

    cdecl

    fastcall

    thiscall
  • 呼び出しプロトコル共通の場合
  • __stdcall:stdcall(pascal)-Standard Callの略で、C++の標準呼び出し方式で、Microsoft C++シリーズのC/C++コンパイラでは、PASCALマクロでこの呼び出し規則を宣言することがよくあります.類似のマクロにはWINAPIやCALLBACKもあります.一般WIN 32の関数はすべて__ですstdcall.
  • __cdecl:cdecl–C Declarationの略、C言語のデフォルトの呼び出し規則、C/C++デフォルトの関数呼び出しプロトコル.
  • __fastcall:性能に対する要求が高い場合に適しています.
  • __thiscall:C++クラスメンバー関数のデフォルトの呼び出し規則.thiscallはキーワードではないため、明確に指定できない唯一の関数修飾です.メンバー関数呼び出しにはthisポインタがあるため、
  • を特別に処理する必要があります.
  • 関数パラメータスタック方式
  • __stdcall:関数パラメータは右から左にスタックされます.パラメータ個数が固定されている場合はstdcallに類似し,不定時はcdeclに類似する.
  • __cdecl:関数パラメータは右から左にスタックされます.パラメータ個数が固定されている場合はstdcallに類似し,不定時はcdeclに類似する.
  • __fastcall:左から4バイト以下のパラメータをCPUのECXとEDXレジスタに入れ、残りのパラメータを右から左にスタックします.fastcallはレジスタに4バイト以下のパラメータを入れるため、性能が高く、高性能が必要な場合に適しています.
  • __thiscall:パラメータは右から左にスタックに入ります.パラメータの個数が不確定な場合、thisポインタはすべてのパラメータのスタックを押した後にスタックに押し込まれます.パラメータの個数が決定されると、thisポインタはecxを介して呼び出し元に渡されます.

  • スタック内データ消去方式
  • __stdcall:関数呼び出しが終了すると、呼び出された関数によってスタック内のデータが消去されます.
  • __cdecl:関数呼び出しが終了すると、関数呼び出し者によってスタック内のデータが消去されます.
  • __thiscall:パラメータの個数が不確定な場合、呼び出し者はスタックをクリーンアップします.そうしないと、関数はスタックを自分でクリーンアップします.
  • __fastcall:関数呼び出しが終了すると、呼び出された関数によってスタック内のデータが消去されます.
  • 問題1:異なるコンパイラによって設定されたスタック構造が異なり、開発プラットフォームにまたがるときに関数呼び出し者によってスタック内のデータを消去することはできない.
  • 問題2:printf関数のようないくつかの関数のパラメータは可変であり、このような関数は関数呼び出し者によってスタック内のデータを消去するしかない.
  • 問題3:呼び出し者によってスタック内のデータを消去する場合、呼び出すたびにスタック内のデータを消去するコードが含まれるため、実行可能ファイルが大きい.

  • C言語コンパイラ関数名修飾規則
  • __stdcall:コンパイル後、関数名が「functionname@number”.
  • __cdecl:コンパイル後、関数名は「_functionname」に変更されます.
  • __fastcall:コンパイル後、関数名を「@に修飾するfunctionname@nmuber”.
  • 注:「functionname」は関数名、「number」はパラメータバイト数です.
  • 注:関数実装と関数定義で異なる関数呼び出しプロトコルが使用されている場合、関数呼び出しは実現できません.

  • C++言語コンパイラ関数名修飾規則
  • __stdcall:コンパイル後、関数名は「?functionname@@YG*****@Z」に修飾されます.
  • __cdecl:コンパイル後、関数名は「?functionname@@YA*****@Z」に修飾されます.
  • __fastcall:コンパイル後、関数名は「?functionname@@YI*****@Z」に修飾されます.
  • 注意:「******」は、関数の戻り値タイプとパラメータタイプのテーブルです.
  • 注:関数実装と関数定義で異なる関数呼び出しプロトコルが使用されている場合、関数呼び出しは実現できません.
  • C言語とC++言語の間で特別な処理を行わなければ,関数の相互呼び出しも実現できない.


  • 具体的には、次の表を参照してください.
    C++            :
    
        __stdcall    :
    
             “?”        ,     ;
    
                  “@@YG”        ,     ;
    
                    :
    
                X–void ,
    
                D–char,
    
                E–unsigned char,
    
                F–short,
    
                H–int,
    
                I–unsigned int,
    
                J–long,
    
                K–unsigned long,
    
                M–float,
    
                N–double,
    
                _N–bool,
    
                PA–    ,           ,             , “0”  ,  “0”      
                …
    
                             ,            ,             ;
    
                 “@Z”         ,        ,  “Z”    。
    
          :
    
        int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”
        void Test2() -----“?Test2@@YGXXZ”
    
        __cdecl    :
              _stdcall    ,              “@@YG”  “@@YA”。
    
        __fastcall    :
              _stdcall    ,              “@@YG”  “@@YI”。
    
                 
    
                    ,         ,      ,          :
    
                       
        DLL               
    
                              ,        。