24

ヘッダー ファイルで関数を宣言し、その関数の定義を他のファイルに配置すると、コンパイラ/リンカーはどのように定義を見つけますか? パス内のすべてのファイルを体系的に検索しますか、それともより洗練されたソリューションがありますか? これはここ数日間私を悩ませてきましたが、その説明を見つけることができませんでした.

4

3 に答える 3

29

コンパイラはこれを行いません。リンカが行います。

コンパイラは一度に 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 だけ) のシンボル テーブルを調べ、foofoo.o を見つけて、リンク。

ライブラリの場合、これはもう少し複雑です。コマンド ラインに表示される順序は、リンカーによって異なりますが、一般的には同じ原則です。

于 2010-07-30T01:18:53.407 に答える
16

.cpp ファイルをコンパイルすると、コンパイラは .obj ファイルに 2 つのテーブルを出力します。外部で定義されると予想されるシンボルのリストと、その特定のモジュールで定義されているシンボルのリストです。

リンカーは、コンパイラによって出力されたすべての .obj ファイルを取得し、(名前が示すように)それらをすべてリンクします。したがって、モジュールごとに、「外部で定義された」とマークされたシンボルのリストを調べ、それらのシンボルに指定された他のすべてのモジュールを調べます。

したがって、検索するように指示したモジュールのみを「検索」します。

他のモジュールのいずれにもシンボルが見つからない場合は、「未定義の参照」エラーが発生します。

于 2010-07-30T01:19:10.613 に答える
0

#include foo.h とおそらく他のインクルードを持つ foo.cpp があると仮定します。もちろん、ヘッダーには独自の #include-s を含めることができます。

プリプロセッサは foo.cpp から開始し、#includes を解析してヘッダーの内容を読み取ります。結果は、ヘッダー ファイルからのテキストであり、foo.cpp は「フラット化」されます。その後、コンパイラはそのテキストを処理します。変数/関数/etc がヘッダーのどこかで宣言されている必要がある場合、コンパイラはエラーを報告します。

基本的なポイントは、コンパイラが .cpp とヘッダーの結果としてすべての宣言を確認する必要があることです。

于 2010-07-30T07:10:01.500 に答える