13

クラスのインターフェイスのすぐ上にあるヘッダーファイルでC関数を定義しようとすると、常にビルドエラーが発生します。

しかし、実装ファイルで同じことを行い、ヘッダーで宣言を行うと。物事はうまくいく。

知りたかったのですが、ヘッダーファイルに列挙型、構造体、定数NSStringを定義したので、なぜC関数ではないのでしょうか。

4

1 に答える 1

24

これは、Cリンカー(またはリンクエディター)の動作方法と関係があります。Cコンパイラは、関数定義に出くわすと、その関数を実装するアセンブラコードを準備し、リンカーに「この名前の関数が開始する場所です」という記号でマークを付けます。シンボルは通常、アンダースコアの後に関数名が続く名前が付けられます(例:)_printf

ヘッダーファイルで関数を定義すると、このヘッダーをインポートするすべてのファイル.cまたは.mファイルが関数をコンパイルし、コンパイラーに同じシンボルを出力させます。リンカは、各シンボルのインスタンスを1つだけ検出することを想定しているため、これはエラーです。

これは、#includeガードの存在や、#importの代わりに使用することとは関係ありません#include。Cコンパイラは、個々の変換ユニットで動作します。つまり、個々のソースファイルを意味します。プリプロセッサ戦略では、同じヘッダーファイルを1つのソースファイルに2回インクルードすることはできませんが、複数のファイル間でアクティビティを調整することはできません。これは、異なるソースファイルに同じヘッダーを含めることが有効であることを意味します。つまり、異なるファイルをコンパイルするときに、(合法的に)同じシンボルを含めることができることも意味します。

これらのファイルをまとめて、コンパイル時に不明だったシンボルへの参照を解決するのは、リンクエディタの仕事です。同じシンボルを持つオブジェクト(コンパイルおよびアセンブルされた変換ユニットの名前)を同じアーカイブ、共有ライブラリ、または実行可能ファイルにリンクしようとすると、ここに表示されているエラーが発生します。

ソリューション:

  • ヘッダーで関数を定義するのではなく、そこで宣言して実装ファイルで定義するだけです。あなたはすでにこれがうまくいくのを見つけたので。
  • ヘッダーで関数を定義しますが、そのヘッダーはコードの1か所にのみ含めてください。これは、設計上の理由から受け入れられないことがよくあります。
  • 修飾子を使用してヘッダーで関数を定義しますinline。インライン関数は、コンパイラーによって呼び出された関数にコピーされるだけなので、リンカーシンボルが出力されることはありません。これには独自のトレードオフがあり、についてもっと読みたいと思うかもしれません。
于 2012-04-20T08:06:31.660 に答える