2009-08-20 9 views
48

最近私は多くの人に__stdcallを送りました。
私の意見では、MSDNは実際に何を意味するのかを非常にはっきりと説明していません。
いつ、なぜそれを使うべきですか?__stdcallの意味と使用法は何ですか?

誰かが説明を提供していただければ幸いです。好ましくは1つまたは2つの例があります。 WinAPIの機能が正常に呼び出される必要がある呼び出し規約です

おかげ

答えて

45

C/C++のすべての関数特定の呼び出し規約があります。呼び出し規約のポイントは、呼び出し元と呼び出し先の間でデータがどのように渡されるか、呼び出しスタックを駆除するなどの操作を担当するユーザーを設定することです。 Windows上の

最も人気のある呼び出し規約は、基本的に伝え関数宣言には、この指定子を追加する

  • STDCALL
  • CDECL
  • clrcall
  • fastcall
  • thiscall

ですあなたのコンパイラこの特定の関数がこの特定の呼び出し規約を持つようにします。

呼び出し規約は、ここで記述されてい

レイモンド・チェンも、ここに始まるさまざまな呼び出し規約の歴史上の長いシリーズ(5部)を行いました。

1

。呼び出し規約は、パラメーターが関数にどのように渡されるか、関数から戻り値がどのように渡されるかに関する規則のセットです。

呼び出し元と呼び出されたコードが異なる規則を使用する場合、未定義の動作(such a strange-looking crashなど)が発生します。

C++コンパイラは、デフォルトで__stdcallを使用しません。他の規則を使用します。したがって、WinAPI関数をC++から呼び出すには、__stdcallを使用するように指定する必要があります。これは通常Windoes SDKヘッダーファイルで行われ、関数ポインタを宣言するときにも行います。

3

関数の呼び出し規約を指定します。呼び出し規約は、パラメータが関数にどのように渡されるかの規則のセットです。アドレス、コピーごとに、パラメータ(呼び出し元または呼び出し先)をクリーンアップする人などです。

5

残念ながら、それを使用していないときにするときのために簡単な答えはありません。

__stdcallは、関数への引数が最初から最後までスタックにプッシュされることを意味します。これは、引数が最後から最初にプッシュされることを意味する__cdeclと反対であり、__fastcallは最初の4つの引数をレジスタに置き、残りはスタックに置かれます。

呼び出し先が期待していること、またはライブラリを作成している場合は、呼び出し元が期待していることを確認して、選択した規約を文書化してください。

+2

'__stdcall'と' __cdecl'は返却後のクリーンアップ(およびデコレーション)の責任のみが異なります。引数の受け渡しは、両方向(右から左)で同じです。あなたが記述しているのはパスカル呼び出し規約です。 – a3f

6

__stdcallは、パラメータが関数(スタックまたはレジスタ上)にどのように渡され、関数が返った後のクリーンアップ(呼び出し元または呼び出し先)を決定する方法です。

レイモンド・チェンはblog about the major x86 calling conventionsと書いてあります。CodeProject articleもあります。

ほとんどの場合、それらについて心配する必要はありません。あなたがデフォルト以外のものを使用するライブラリ関数を呼び出す場合は、コンパイラが間違ったコードを生成し、プログラムがクラッシュする可能性があります。

2

__stdcallは呼び出し規約を示します(詳細はthis PDFを参照)。つまり、関数の引数がスタックからどのようにプッシュされ、スタックからポップされ、誰が責任を負うのかを指定します。

__stdcallは、いくつかの呼び出し規約の1つに過ぎず、WINAPI全体で使用されています。これらの関数のいくつかのコールバックとして関数ポインタを提供する場合は、それを使用する必要があります。一般に、あなたのコードで特定の呼び出し規約を示す必要はありませんが、上記のケース(サードパーティコードへのコールバックを提供する)を除き、コンパイラのデフォルトを使用するだけです。

1

関数を呼び出すと単純に置くとスタック/レジスタにロードされます。 __stdcallは1つの規則/方法(右の引数を最初に、次に左の引数...)ですが、__declはスタックまたはレジスタに関数をロードするために使用される別の規則です。

リンクを使用している場合は、リンク時に関数をロード/アンロードするためにコンピュータにその特定の方法を使用するように指示するため、不一致/クラッシュは発生しません。

それ以外の場合、function-calleeとfunction-callerは、プログラムをクラッシュさせるさまざまな規則を使用することがあります。

33

従来、C関数呼び出しは、呼び出し側がいくつかのパラメータをスタックにプッシュし、関数を呼び出してからスタックをポップしてプッシュされた引数をクリーンアップすることで行われます。

/* example of __cdecl */ 
push arg1 
push arg2 
push arg3 
call function 
add sp,12 // effectively "pop; pop; pop" 

注:上記の既定の規約は、__cdeclとして知られています。

他の最も一般的な規約は__stdcallです。その中で、パラメータは再び呼び出し元によってプッシュされますが、スタックは呼び出し先によってクリーンアップされます。 Win32 API関数(WINAPIマクロで定義されている)の標準的な規則であり、「パスカル」呼び出し規約と呼ばれることもあります。

/* example of __stdcall */ 
push arg1 
push arg2 
push arg3 
call function // no stack cleanup - callee does this 

これはマイナーな技術的な詳細のように見えますが、スタックは、呼び出し元と呼び出し先の間で管理されている方法についての意見の相違がある場合、スタックは回復されにくい方法で破棄されます。 __stdcallはスタッククリーンアップを実行するので、このタスクを実行する(ごくわずかな)コードは、__cdeclにあるようにすべての呼び出し元で複製されるのではなく、1つの場所にのみ存在します。サイズの影響は大きなプログラムでのみ表示されますが、これによりコードは非常にわずかに小さくなります。

printf()のようなバリアント関数は、__stdcallで正しく取得することはほとんど不可能です。なぜなら、呼び出し元だけが、それらをクリーンアップするために渡された引数の数を実際に知っているからです。呼び出し先は、(フォーマット文字列を見て)いくつかの良い推測をすることができますが、スタックのクリーンアップは、関数の実際のロジックで決める必要があります。したがって、__cdeclだけが可変長関数をサポートしているため、呼び出し元はクリーンアップを実行できます。

リンカのシンボル名の飾り付け: 上記の箇条書きで説明したように、「間違った」規則で関数を呼び出すと悲惨な結果になる可能性があります。それはうまくいきますが、その理由がわからないと狂ってしまうかもしれません。 彼らは、呼び出し規約を、余分な文字(しばしば「装飾」と呼ばれる)を持つ低レベルの関数名にエンコードすることでこれを解決することを選択しました。これらはリンカーによって無関係な名前として扱われます。デフォルトの呼び出し規約は__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);   -----------------------> [email protected] 

void __stdcall foo(int a);   -----------------------> [email protected] 

void __stdcall foo(int a, int b); -----------------------> [email protected] 

void __fastcall foo(void);   -----------------------> @[email protected] 

void __fastcall foo(int a);  -----------------------> @[email protected] 

void __fastcall foo(int a, int b); -----------------------> @[email protected] 
1

__stdcall機能に使用される呼び出し規約です。これは、スタックの設定、引数のプッシュ、戻り値の取得に適用される規則をコンパイラに指示します。 __cdecl__thiscall、と __naked __fastcallのような他の呼び出し規約がいくつかあります。

__stdcallは、Win32システムコールの標準呼び出し規約です。

詳細はWikipediaにあります。

関連する問題