クラスのインターフェイスのすぐ上にあるヘッダーファイルでC関数を定義しようとすると、常にビルドエラーが発生します。
しかし、実装ファイルで同じことを行い、ヘッダーで宣言を行うと。物事はうまくいく。
知りたかったのですが、ヘッダーファイルに列挙型、構造体、定数NSStringを定義したので、なぜC関数ではないのでしょうか。
クラスのインターフェイスのすぐ上にあるヘッダーファイルでC関数を定義しようとすると、常にビルドエラーが発生します。
しかし、実装ファイルで同じことを行い、ヘッダーで宣言を行うと。物事はうまくいく。
知りたかったのですが、ヘッダーファイルに列挙型、構造体、定数NSStringを定義したので、なぜC関数ではないのでしょうか。
これは、Cリンカー(またはリンクエディター)の動作方法と関係があります。Cコンパイラは、関数定義に出くわすと、その関数を実装するアセンブラコードを準備し、リンカーに「この名前の関数が開始する場所です」という記号でマークを付けます。シンボルは通常、アンダースコアの後に関数名が続く名前が付けられます(例:)_printf
。
ヘッダーファイルで関数を定義すると、このヘッダーをインポートするすべてのファイル.c
または.m
ファイルが関数をコンパイルし、コンパイラーに同じシンボルを出力させます。リンカは、各シンボルのインスタンスを1つだけ検出することを想定しているため、これはエラーです。
これは、#include
ガードの存在や、#import
の代わりに使用することとは関係ありません#include
。Cコンパイラは、個々の変換ユニットで動作します。つまり、個々のソースファイルを意味します。プリプロセッサ戦略では、同じヘッダーファイルを1つのソースファイルに2回インクルードすることはできませんが、複数のファイル間でアクティビティを調整することはできません。これは、異なるソースファイルに同じヘッダーを含めることが有効であることを意味します。つまり、異なるファイルをコンパイルするときに、(合法的に)同じシンボルを含めることができることも意味します。
これらのファイルをまとめて、コンパイル時に不明だったシンボルへの参照を解決するのは、リンクエディタの仕事です。同じシンボルを持つオブジェクト(コンパイルおよびアセンブルされた変換ユニットの名前)を同じアーカイブ、共有ライブラリ、または実行可能ファイルにリンクしようとすると、ここに表示されているエラーが発生します。
ソリューション:
inline
。インライン関数は、コンパイラーによって呼び出された関数にコピーされるだけなので、リンカーシンボルが出力されることはありません。これには独自のトレードオフがあり、についてもっと読みたいと思うかもしれません。