出会った __stdcall
最近はたくさん。
MSDNでは、それが実際に何を意味するのか、いつ、なぜ使用する必要があるのか、まったく説明していません。
誰かが説明を提供してくれれば幸いです。できれば1つまたは2つの例があります。
C/C++のすべての関数には、特定の呼び出し規則があります。呼び出し規約のポイントは、呼び出し元と呼び出し先の間でデータが渡される方法と、呼び出しスタックの消去などの操作の責任者を確立することです。
Windowsで最も一般的な呼び出し規則は次のとおりです。
__stdcall
、逆順(右から左)でスタックにパラメーターをプッシュします__cdecl
、逆順(右から左)でスタックにパラメーターをプッシュします__clrcall
、パラメーターをCLR式スタックに順番にロードします(左から右)。__fastcall
、レジスタに保存され、スタックにプッシュされます__thiscall
、スタックにプッシュ。 ECXに保存されたこのポインターこの指定子を関数宣言に追加すると、本質的に、この特定の関数にこの特定の呼び出し規約を持たせたいとコンパイラーに伝えます。
呼び出し規約はここに文書化されています
レイモンドチェンは、ここから始まるさまざまな呼び出し規約(5つのパート)の歴史に関する長いシリーズも行いました。
従来、C関数呼び出しは、呼び出し側がいくつかのパラメーターをスタックにプッシュし、関数を呼び出し、スタックをポップしてプッシュされた引数をクリーンアップすることで行われていました。
/* example of __cdecl */
Push arg1
Push arg2
Push arg3
call function
add sp,12 // effectively "pop; pop; pop"
注:上記のデフォルトの規則は__cdeclとして知られています。
他の最も一般的な規則は__stdcallです。その中で、パラメーターは呼び出し元によって再びプッシュされますが、スタックは呼び出し先によってクリーンアップされます。これは、Win32 API関数の標準規則(のWINAPIマクロで定義されている)であり、「Pascal」呼び出し規則とも呼ばれます。
/* example of __stdcall */
Push arg1
Push arg2
Push arg3
call function // no stack cleanup - callee does this
これはマイナーな技術的詳細のように見えますが、呼び出し元と呼び出し先の間でスタックがどのように管理されるかについて意見の相違がある場合、スタックは回復されそうにない方法で破棄されます。 __stdcallはスタックのクリーンアップを行うため、このタスクを実行する(非常に小さな)コードは、__ cdeclのようにすべての呼び出し元で複製されるのではなく、1か所でしか見つかりません。これにより、コードが非常にわずかに小さくなりますが、サイズの影響は大きなプログラムでのみ確認できます。
Printf()のような可変個性関数は__stdcallで正しく動作することはほとんど不可能です。なぜなら、それらをクリーンアップするために渡された引数の数を本当に知っているのは呼び出し元だけだからです。呼び出し先は(たとえば、フォーマット文字列を見て)ある程度推測できますが、スタックのクリーンアップは、呼び出し規則メカニズム自体ではなく、関数の実際のロジックによって決定する必要があります。したがって、呼び出し元がクリーンアップを実行できるように、__cdeclのみが可変機能関数をサポートします。
リンカーシンボル名の装飾:上記の箇条書きで述べたように、「間違った」規則で関数を呼び出すと悲惨なことがあるため、Microsoftにはこれを回避するメカニズムがあります。それはうまく機能しますが、理由がわからない場合は気が狂います。彼らはこれを解決するために、呼び出し規約を余分な文字(しばしば「装飾」と呼ばれる)を含む低レベル関数名にエンコードし、これらはリンカによって無関係な名前として扱われます。デフォルトの呼び出し規則は__cdeclですが、各呼び出しは/ G?を使用して明示的に要求できます。コンパイラへのパラメータ。
__cdecl(cl/Gd ...)
このタイプの関数名にはすべてアンダースコアが付いており、呼び出し側がスタックのセットアップとクリーンアップを担当するため、パラメーターの数は実際には重要ではありません。実際に渡されるパラメーターの数について、呼び出し元と呼び出し先が混同される可能性がありますが、少なくともスタックの規律は適切に維持されます。
__stdcall(cl/Gz ...)
これらの関数名には下線が接頭辞として付けられ、@と渡されたパラメーターのバイト数が付加されます。このメカニズムにより、「間違った」タイプの関数を呼び出したり、パラメーターの数が間違っていたりすることはできません。
__fastcall(cl/Gr ...)
これらの関数名は@記号で始まり、__ stdcallのように@parameterカウントがサフィックスとして付けられます。
例:
Declaration -----------------------> decorated name
void __cdecl foo(void); -----------------------> _foo
void __cdecl foo(int a); -----------------------> _foo
void __cdecl foo(int a, int b); -----------------------> _foo
void __stdcall foo(void); -----------------------> _foo@0
void __stdcall foo(int a); -----------------------> _foo@4
void __stdcall foo(int a, int b); -----------------------> _foo@8
void __fastcall foo(void); -----------------------> @foo@0
void __fastcall foo(int a); -----------------------> @foo@4
void __fastcall foo(int a, int b); -----------------------> @foo@8
__stdcallは呼び出し規約です。(スタックまたはレジスター内の)関数にパラメーターを渡す方法と、関数が戻った後のクリーンアップの責任者(呼び出し元または呼び出し先)を決定する方法です。
Raymond Chenが x86の主要な呼び出し規約についてのブログ を書いており、Nice CodeProjectの記事 もあります。
ほとんどの場合、それらについて心配する必要はありません。必要な唯一のケースは、デフォルト以外のものを使用するライブラリ関数を呼び出す場合です。そうしないと、コンパイラが間違ったコードを生成し、プログラムがクラッシュする可能性があります。
残念ながら、いつ使用するか、使用しないかについて簡単な答えはありません。
__stdcallは、関数の引数が最初から最後までスタックにプッシュされることを意味します。これは、引数が最後から最初にプッシュされることを意味する__cdeclと、レジスタに最初の4つの(と思う)引数を配置し、残りがスタックに配置される__fastcallとは対照的です。
必要なのは、呼び出し先が何を期待しているか、またはライブラリを作成している場合、呼び出し元が何を期待しているのかを知り、選択した規則を文書化することだけです。
関数の呼び出し規約を指定します。呼び出し規則は、パラメーターが関数に渡される方法のセットです:順序、アドレスごと、またはコピーごと、誰がパラメーター(呼び出し元または呼び出し先)をクリーンアップするかなど.
__stdcallは呼び出し規約を示します(詳細については this PDF を参照)。これは、スタックから関数の引数をプッシュおよびポップする方法、および責任者を指定することを意味します。
__stdcallは、いくつかの呼び出し規約の1つにすぎず、WINAPI全体で使用されます。これらの関数の一部のコールバックとして関数ポインターを提供する場合は、これを使用する必要があります。一般に、コード内で特定の呼び出し規約を示す必要はありませんが、上記のケース(サードパーティコードへのコールバックを提供する場合)を除き、コンパイラのデフォルトを使用するだけです。
__ stdcallは、関数に使用される呼び出し規約です。これは、スタックのセットアップ、引数のプッシュ、および戻り値の取得に適用されるルールをコンパイラーに伝えます。 __ cdecl、__ thiscall、__ fastcallおよび__ naked。
__ stdcallは、Win32システムコールの標準呼び出し規約です。
詳細は Wikipedia で見つけることができます。
これは、WinAPI関数を適切に呼び出す必要がある呼び出し規則です。呼び出し規約は、パラメーターが関数に渡される方法と戻り値が関数から渡される方法に関する一連の規則です。
呼び出し元と呼び出されたコードが異なる規則を使用している場合、未定義の動作が発生します( そのような奇妙なクラッシュ など)。
C++コンパイラは、デフォルトでは__stdcallを使用しません-他の規則を使用します。したがって、C++からWinAPI関数を呼び出すには、__ stdcallを使用するように指定する必要があります。これは通常、Windoes SDKヘッダーファイルで行われ、関数ポインターを宣言するときにも行われます。
関数を呼び出すときに置くだけで、スタック/レジスタにロードされます。 __stdcallは1つの規則/方法(最初に右引数、次に左引数...)、__ declは関数をスタックまたはレジスターにロードするために使用される別の規則です。
それらを使用する場合、リンク中に関数をロード/アンロードする特定の方法を使用するようにコンピューターに指示するため、不一致/クラッシュは発生しません。
そうしないと、関数呼び出し先と関数呼び出し元が異なる規則を使用して、プログラムがクラッシュする可能性があります。