ヘッダー ファイルで関数を宣言し、その関数の定義を他のファイルに配置すると、コンパイラ/リンカーはどのように定義を見つけますか? パス内のすべてのファイルを体系的に検索しますか、それともより洗練されたソリューションがありますか? これはここ数日間私を悩ませてきましたが、その説明を見つけることができませんでした.
3 に答える
コンパイラはこれを行いません。リンカが行います。
コンパイラは一度に 1 つのソース ファイルを処理しますが、リンカーが呼び出されると、コンパイラによって生成されたすべてのオブジェクト ファイルの名前と、ユーザーがリンクしたいライブラリの名前が渡されます。したがって、リンカーは、定義を含む可能性のある一連のファイルを完全に認識しており、それらのオブジェクト ファイルのシンボル テーブルを調べるだけで済みます。それ以上の検索は必要ありません。
たとえば、 function を定義して実装する foo.h と foo.c と、 functionfoo()
を定義して実装する bar.h と bar.c があるとしますbar()
。bar.c に foo.h が含まれるように呼び出しbar
ます。foo
このコンパイルには 3 つのステップがあります。
gcc -c foo.c
gcc -c bar.c
gcc foo.o bar.o -o program
最初の行は foo.c をコンパイルし、foo.o を生成します。2 番目は bar.c をコンパイルし、bar.o を生成します。この時点で、オブジェクト ファイル bar.o 内のfoo
は外部シンボルです。3 行目はリンカーを呼び出します。このリンカーは、foo.o と bar.o をリンクして「program」という実行可能ファイルにします。リンカが bar.o を処理するとき、未解決の外部シンボルfoo
を見つけるので、リンクされている他のすべてのオブジェクト ファイル (この場合は foo.o だけ) のシンボル テーブルを調べ、foo
foo.o を見つけて、リンク。
ライブラリの場合、これはもう少し複雑です。コマンド ラインに表示される順序は、リンカーによって異なりますが、一般的には同じ原則です。
.cpp ファイルをコンパイルすると、コンパイラは .obj ファイルに 2 つのテーブルを出力します。外部で定義されると予想されるシンボルのリストと、その特定のモジュールで定義されているシンボルのリストです。
リンカーは、コンパイラによって出力されたすべての .obj ファイルを取得し、(名前が示すように)それらをすべてリンクします。したがって、モジュールごとに、「外部で定義された」とマークされたシンボルのリストを調べ、それらのシンボルに指定された他のすべてのモジュールを調べます。
したがって、検索するように指示したモジュールのみを「検索」します。
他のモジュールのいずれにもシンボルが見つからない場合は、「未定義の参照」エラーが発生します。
#include foo.h とおそらく他のインクルードを持つ foo.cpp があると仮定します。もちろん、ヘッダーには独自の #include-s を含めることができます。
プリプロセッサは foo.cpp から開始し、#includes を解析してヘッダーの内容を読み取ります。結果は、ヘッダー ファイルからのテキストであり、foo.cpp は「フラット化」されます。その後、コンパイラはそのテキストを処理します。変数/関数/etc がヘッダーのどこかで宣言されている必要がある場合、コンパイラはエラーを報告します。
基本的なポイントは、コンパイラが .cpp とヘッダーの結果としてすべての宣言を確認する必要があることです。