他の場所での議論から:
C ++には標準のABI(アプリケーションバイナリインターフェイス)がありません
しかし、Cもそうではありませんか?
どのプラットフォームでも、ほとんどそうです。言語間コミュニケーションの共通語としては、それがなければ役に立たないでしょう。
これについてどう思いますか?
CはABIを定義しません。実際、ABIの定義を回避するために、後方に曲がっています。プログラミングのほとんどをCで8ビットバイト、2の補数演算、フラットアドレス空間を備えた16/32/64ビットアーキテクチャでプログラミングしてきた私のような人々は、通常、の複雑な言語を読んで非常に驚かれることでしょう。現在のC標準。
たとえば、ポインタに関するものを読んでください。この規格では、「ポインタはアドレスである」という単純なことは何も述べていません。そのため、ABIについての仮定があります。特に、ポインタが異なるアドレス空間にあり、幅が異なる場合があります。
ABIは、言語の実行モデルから特定のマシン/オペレーティングシステム/コンパイラの組み合わせへのマッピングです。一部のアーキテクチャでC実装を除外するリスクがあるため、言語仕様で1つを定義することは意味がありません。
Cには原則として標準のABIはありませんが、実際には、これが問題になることはめったにありません。OSベンダーが行うことを実行します。
x86 Windowsの呼び出し規約を例にとってみましょう。WindowsAPIは、いわゆる「標準」の呼び出し規約(stdcall)を使用します。したがって、OSとのインターフェースを希望するコンパイラーは、それを実装する必要があります。ただし、stdcallはすべてのC90言語機能をサポートしているわけではありません(たとえば、プロトタイプなしの関数の呼び出し、可変個引数関数)。MicrosoftがCコンパイラを提供したため、「C」呼び出し規約(cdecl)と呼ばれる2番目の呼び出し規約が必要でした。Windows上のほとんどのCコンパイラは、これをデフォルトの呼び出し規約として使用するため、相互運用可能です。
原則として、同じことがC ++でも発生する可能性がありますが、C ++ ABI(呼び出し規約を含む)は必然的にはるかに複雑であるため、コンパイラベンダーは単一のABIに同意しませんでしたが、にフォールバックすることで相互運用できextern "C"
ます。
ABI for Cはプラットフォーム固有であり、レジスタ割り当てや呼び出し規約など、明らかに特定のプロセッサに固有の問題をカバーしています。ここではいくつかの例を示します。
x86には多くの呼び出し規約があり、Windowsでどの拡張機能を使用するかを宣言します。組み込みLinuxのプラットフォームABIも時間の経過とともに変化し、互換性のないユーザースペースにつながっています。新しいABIへの移行における問題を示すARMLinuxポートの履歴をここで参照してください。
複数のオペレーティングシステム(特にUnixシステム上のi386)で、特定のアーキテクチャに対して単一のABIを定義する試みがいくつか行われていますが、そのような成功には至っていません。代わりに、オペレーティングシステムは独自のABIを定義する傾向があります...
引用... Linuxシステムプログラミングページ4。
ABIには、Cの場合でも、プラットフォームにまったく依存しない部分、プロセッサに依存する部分(レジスタを保存する必要があり、パラメータの受け渡しに使用される部分など)、およびOSに依存する部分(多かれ少なかれ)があります。一部の選択はアーキテクチャによって課せられないが、トレードオフの結果であるため、プロセッサの場合と同じ要因に加えて、一部のOSには言語に依存しない例外の概念があるため、任意の言語のコンパイラは処理する適切なものを生成する必要がありますそれら、スレッドの処理もABIに何かを課す可能性があります-レジスタがTLSを指している場合、それを目的に使用することはできません)。
理論的には、すべてのコンパイラが独自のABIを持っている場合があります。ただし、通常、2つのプロセッサ/ OSの場合、ABIはOSベンダーによって修正されます。このベンダーは、ABIと競合他社が互換性を優先することを使用するCコンパイラと共通ライブラリも提供することがよくあります。(Cが主要なプログラミング言語ではない一部のOSに例外があったとしても、私は驚かないでしょう)。
ただし、OSベンダーは何らかの理由でABIを切り替える場合があります(新しいバージョンのプロセッサには、ABIで使用したい機能がある場合があります-たとえば、すべてのレジスタを使用できるx86_64用の32ビットABIを要求する人もいます) 。移行フェーズでは(非常に長い時間がかかる場合があります)、2つのABIを処理する必要がある場合があります。
Cもそうではありませんか?
右
どのプラットフォームでも、ほとんどそうです。言語間コミュニケーションの共通語としては、それがなければ役に立たないでしょう。
ほとんどの場合、他の言語に適合しているCコンパイラベンダーによって選択されたアーキテクチャ固有のデフォルトを参照している可能性があります。したがって、KeilのARM Cコンパイラが左から右へのリトルエンディアンパラメータの順序付けとスタックを使用して引数と戻り値の所定のレジスタを渡す場合、他のコンパイラのextern"C"はそのようなスキームとの互換性を前提としています。
このような合意はABIの一部と見なされる可能性がありますが、JVMブラウザサンドボックスなどの管理された実行コンテキストとは異なり、これはそれ自体で完全な標準ABIとはほど遠いものです。
Cには標準のABIがありません。これは、そこで使用されているすべての呼び出し規約(cdecl、fastcall、stdcall)によって簡単に説明できます。それぞれが異なるABIです。
C89標準より前は、多くのプラットフォームのCコンパイラは、データサイズのばらつきを除いて、基本的に同じABIを使用していました。スタックが下に大きくなるマシンの場合、関数を呼び出すコードは、スタック上の引数を右から左に順番にプッシュしてから、関数を呼び出します(プロセスでリターンアドレスをプッシュします)。呼び出された関数は引数をスタックに残し、呼び出し元はスタックポインタを調整してそれらを削除します[または、一部のアーキテクチャでは、スタックされた値を所定の位置に調整する場合があります]。その間<stdarg.h>
ほとんどのプログラムがその規則に依存する必要がなくなったため、シンプルで非常にうまく機能したため、長年使用され続けました。クロスプラットフォームの「標準」としてそれを確立する「公式」ドキュメントはありませんでしたが、スタックが減少するマシンを対象とするほとんどのコンパイラはそのように機能し、現在よりも高いレベルの一貫性をもたらしました。
Cは常に最大のランタイムパフォーマンスであり、最高のパフォーマンスを発揮するABIは基盤となるハードウェアに依存するため、標準のABIはありません。その結果、ABIはスタックのみを使用するか、特定のハードウェアの必要に応じて関数呼び出し引数と戻り値を渡すためにレジスタを優先する場合があります。
たとえば、amd64(別名x86-64)でさえ、Microsoftx64とSystemVAMD64ABIの2つの呼び出し規約があります。前者は4つの最初の引数をレジスタに置き、残りをスタックに入れます。後者は、6つの最初の引数をレジスタに置き、残りをスタックに入れます。Microsoftがamd64ハードウェアに対して互換性のない呼び出し規約を作成した理由がわかりません。私が知っている限りでは、Microsoftの亜種はパフォーマンスが少し劣り、後で作成されました。
詳細については、https://en.wikipedia.org/wiki/X86_calling_conventionsを参照してください。