構造体の二重宣言がコンパイルエラーを引き起こすのに、関数の二重定義がリンクエラーを引き起こすのはなぜですか?
4 に答える
関数定義はリンク時に実行可能ファイルに含まれますが、デカレーションまたは構文チェックはすべてコンパイル時に行われます。
関数を呼び出していて、コンパイラが関数の宣言を見つけることができない場合にも、1つのことを考慮してください。そうすると、警告がとして生成されimplicit declaration of func()
ます。
この警告メッセージを削除するために、funcの前方宣言を提供し、int func();
警告メッセージなしでコンパイルしました。
なぜこれが起こると思いますか?func()
これは、コンパイラがそのシンボルを見つけられなかったために発生します。言語の文法に従ってコードエラーをなくすのは、すべてコンパイラー次第です。
しかし、最終的な実行可能ファイルのビルドはリンク時に行われ、リンカーはの関数定義を探し始めfunc()
ました。見つかった場合は問題なく、そうでない場合は..Linker error
could not have resolved external symbol _func()
注:外部シンボルはリンク時に解決されます
コンパイルのみのgccでは、これを使用します:(これはコンパイラによって異なる場合があります)
gcc -Werror -c test.c
->test.o
ファイルを生成します
次に、それをリンクして実行可能にしてみてください
gcc -Werror -o test test.o
->test
実行可能
標準では、エラーをいつ報告する必要があるかは規定されていません。コンパイラ次第ですが、基本的には、エラーが検出されるのはそのためです。
まず、コンパイラがファイルを解析します。struct
またはclass
が同じ翻訳単位で複数回定義されているかどうかを簡単に確認できます(これはエラーです。翻訳単位間では、複数のクラスタイプ定義を使用できます)。これは、その翻訳単位を処理するためです。
次に、オブジェクトファイルをリンクします(リンク)。同じシンボルが複数回エクスポートされたことがわかるのは今だけです。これは、たとえばエラーが発生したときだからです。
プログラムをコンパイルするとき、コンパイラーは、使用する構造体の正確な定義を知る必要があります。ただし、プログラムをリンクしようとしているときにのみ、どの関数を使用するかを正確に知る必要があります。
したがって、構造体が2回定義されている場合、コンパイラはコンパイル中に混乱するため、コンパイル時に文句を言います。
コンパイル時の関数の場合、複数の定義を持つことができますが、混乱はリンク中にのみ発生するため、リンク中に文句を言います。
あなたが言っていることは必ずしも真実ではありません。
関数定義をヘッダーに「インライン化」してから、その定義をコンパイルユニットに書き込むと、エラーが発生します。
...already has a definition.
参照しているのは、2つの異なるコンパイルユニットが同じ関数を定義(または定義を参照)している場合です。したがって、個々のコンパイルユニットにコンパイルエラーはなく、それらの組み合わせがリンクエラーの原因になります。
ちなみに、これはキーワードinline
が実際に違いを生む場所であることに注意してください。ヘッダーで定義されたテンプレート化されていない関数の場合、inline
キーワードを使用すると、この関数が定義された状態で複数のコンパイル単位が存在できることを意味します。コンパイラがインライン化することを実際に保証するものではありません。