C++ では、可変個引数関数 (パラメーター リストの最後に ... が付いている関数) は必ず __cdecl 呼び出し規則に従う必要がありますか?

StackOverflow https://stackoverflow.com/questions/2512746

質問

__stdcall 関数に省略記号を含めることはできないことはわかっていますが、__cdecl または __stdcall 以外の呼び出し規約で stdarg.h 関数をサポートするプラットフォームがないことを確認したいと思います。

役に立ちましたか?

解決

呼び出し規約は、(呼び出し先が渡されるものを知らないため)、呼び出し側がスタックから引数をクリアして1にする必要があります。

これは必ずしもMicrosoftはかかわらず、「__cdecl」と呼ぶものに対応していません。それは、SPARCが動作するように設計された方法ですので、ちょうど例えば、SPARC上で、それは通常、レジスタ内の引数を渡すだろう - そのレジスタは基本的に通話が深い十分なものを取得する場合、メインメモリにスピルされるコールスタックとして機能します彼らはもうレジスタに収まりません。

私はそれについてあまり確信しているものの、

は、私がIA64(Itaniumベース)上のほぼ同じ期待 - それはまた、巨大なレジスタセット(数百人のメモリが提供されている場合)を持っています。私は間違っていない場合、それはもう少し寛容あなたはレジスタを使用する方法についてですが、私はそれが、少なくとも同様に多くの時間を使用することを期待したい。

なぜにこの問題をしますか?それは移植性、可変引数を扱うことができますので、STDARG.H、そのマクロを使用してのポイントは、あなたのコードから呼び出し規約で非表示の違いにある。

コメントに基づいて

編集:さて、私はあなたが(答えを改善するために、少なくとも十分に)やっていることを理解しています。あなたは既に(明らかに)デフォルトABIの変動を処理するコードを持っていることを考えると、物事は単純です。それは葉可変長引数の関数は常に手元にプラットフォーム用であることを起こるものは何でも「デフォルトABI」を、使用するかどうかの質問をのみ。 「STDCALL」とのみのオプションとして「デフォルト」で、私はその答えはイエスだと思います。ただ、例えば、Windows上で、wsprintfwprintfは、親指のルールを破る、および使用ではなく、STDCALLのcdecl呼び出し規約ます。

他のヒント

これを判断する最も確実な方法は、呼び出し規約を分析することです。可変個引数関数が機能するには、呼び出し規約にいくつかの属性が必要です。

  • 呼び出し先は、スタックの先頭からの固定オフセットから、可変引数リストの一部ではないパラメーターにアクセスできなければなりません。これには、コンパイラがパラメータをスタックに右から左にプッシュする必要があります。(これには、最初のパラメータなどが含まれます。 printf, 、フォーマット仕様。また、変数引数リスト自体のアドレスも既知の場所から取得する必要があります。)
  • 呼び出し側は、関数が返された後、スタックからパラメータを削除する必要があります。呼び出し側のコードを生成するときに、最初にスタックにプッシュされたパラメータの数を知っているのはコンパイラだけであるためです。可変個引数関数自体にはこの情報はありません。

stdcall 呼び出し先がスタックからパラメータをポップする責任があるため、機能しません。昔の 16 ビット Windows の時代には、 pascal パラメータを左から右にスタックにプッシュするため、機能しません。

もちろん、他の回答がほのめかしているように、多くのプラットフォームでは呼び出し規約の点で選択肢がないため、この質問はそれらのプラットフォームにとっては無関係です。

x86 システム上の次の関数を考えてみましょう。

void __stdcall something(char *, ...);

この関数は、それ自体を __stdcall として宣言します。これは、呼び出し先のクリーンな規則です。ただし、呼び出し先は渡されたパラメーターの数を知らないため、可変引数関数を呼び出し先をクリーンにすることはできません。したがって、クリーンにすべきパラメーターの数もわかりません。

Microsoft Visual Studio C/C++ コンパイラは、呼び出し規約を __cdecl にサイレントに変換することでこの競合を解決します。__cdecl は、非表示のこのパラメーターを受け取らない関数でサポートされている唯一の可変個引数呼び出し規約です。

この変換が警告やエラーを生成せずにサイレントに行われるのはなぜですか?

私の推測では、コンパイラ オプション /Gr (デフォルトの呼び出し規約を __fastcall に設定) と /Gz (デフォルトの呼び出し規約を __stdcall に設定) の煩わしさを軽減するためだと思います。

可変個引数関数の __cdecl への自動変換とは、/Gr または /Gz コマンド ライン スイッチをコンパイラ オプションに追加するだけで、すべてが (新しい呼び出し規約だけで) コンパイルおよび実行されます。

これを考える別の方法は、コンパイラを次のように考えることではありません。 可変個引数 __stdcall を __cdecl に変換するのではなく、単に「可変個引数関数の場合、__stdcall は呼び出し元がクリーンです」と言うだけです。

ここをクリック

「MSVC によってサポートされるプラットフォーム」という意味ですか、それとも一般的な規則ですか?MSVC がサポートするプラットフォームに限定したとしても、次のような状況は依然として発生します。 IA64 そして AMD64 ここで、呼び出し規約は「1 つ」だけあり、その呼び出し規約は と呼ばれます。 __stdcall, 、しかしそれは確かに同じではありません __stdcall x86 にアクセスできます。

私の知る限りでは、呼び出し規約の多様性は、x86上のDOS / Windowsにユニークです。他のほとんどのプラットフォームでは、コンパイラは、OSに付属しているし、大会を標準化していた。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top