1

私は C が初めてで、特に静的ライブラリを作成していて、ライブラリから奇妙な動作が発生しています。

私は cde という小さな静的ライブラリを書きました。gcc を使用してさまざまな部分を .o ファイルにコンパイルし、ar を使用してそれらをすべて .a ファイルにまとめました。

さて、ライブラリをテストしたいときは、次のようにします。

gcc test.c -L../bin -lcde -lelf

libcde.a は、../bin にある私のライブラリです。libelf.a は、自分のライブラリに必要なライブラリです (自分のライブラリに直接配置する方法がわかりません...)。

問題は、ライブラリのヘッダー ファイルをインクルードしなくても、ライブラリのすべての関数を呼び出すことができるということです。そんなことがあるものか?コンパイル時にファイルをリンクしてはならないので、コンパイラはライブラリ内で利用可能な関数を認識できません...

次の方法で実行すると、

gcc -L../bin -lcde -lelf test.c

ファイル test.c は、ヘッダー ファイルをインクルードしたにもかかわらず、ヘッダー ファイルで定義された関数を見つけることができません。

ここで根本的に間違ったことをしていると思いますが、何が原因なのかわかりません。

4

4 に答える 4

9

ここで 2 つの質問があります。

  1. test.cライブラリ内のルーチンの宣言を提供するヘッダーなしでコンパイルするのはなぜですか?
  2. test.cコマンド ラインの最初にリストされているとリンクが機能するのに、コマンド ラインtest.cの最後にリストされているとリンクが機能しないのはなぜですか?

ソースコードを示していないため、最初の質問に対して完全な回答を提供することはできません。他の人が指摘したように、C には、主に歴史的な理由から、暗黙の宣言を提供する余裕があります。これらの暗黙的な宣言は、ルーチンの実際の定義と一致しない可能性があり、エラーが発生する可能性があるため、暗黙的な宣言は避ける必要があります。

2番目の質問に対する答えはこれです。表示された 2 つのコマンド ラインのいずれかを指定すると、コンパイラはコンパイルしtest.cてリンカーを呼び出します。(コンパイラーは、リンクせずにコンパイルしたり、以前にコンパイルされたソースからオブジェクト・モジュールをリンクしたりするなど、他のことを行うようにすることもできます。)それらをリンカーに渡しました。特に、の-lcdetest.cに置くと、コンパイラは、リンカーを実行するときに、から-lcde来るオブジェクト モジュールの前に置きます。test.ctest.o

これは、リンカーの動作方法のために重要です。特に、リンカーには、定義が必要なシンボルのリストがあります。リストは最初は空です。リンカーは、コマンド ラインからの入力を左から右に処理します。リンカは、test.oコマンド ラインなどでオブジェクト モジュールを検出すると、オブジェクト モジュールを読み取り、処理します。多くの場合、オブジェクト モジュールには、ライブラリ ルーチンの呼び出しなど、定義されていないシンボルへの参照が含まれています。リンカーが以前のファイルからのこれらのシンボルの定義を既に持っている場合、参照を定義に接続します。定義がない場合は、リンカーが定義を必要とするシンボルのリストにシンボルを追加します。

リンカーは、ライブラリ ファイルを処理するときに、ライブラリ内の各オブジェクト モジュールをチェックして、そのオブジェクト モジュールがリンカーの必要な定義のリストにあるシンボルを定義しているかどうかを確認します。その場合、リンカーはそのオブジェクト モジュールを読み取り、それ (およびその定義) を、リンカーが構築している実行可能ファイルに追加します。そうでない場合、リンカーはそのオブジェクト モジュールを無視します。

test.c -lcdeこれで、機能するのに機能-lcde test.cしない理由がわかります。前者の場合、リンカはtest.o必要なものすべてのリストを作成し、cdeライブラリからそれらを取得します。後者の場合、リンカはcdeライブラリを認識しますが、まだそこから何も必要としないため、ライブラリから何も取得せずに続行します。次に、リンカーtest.cは必要なシンボルのリストを読み取り、追加します。次に、コマンド ラインが終了し、リンカにはファイルがなくなりますが、定義のないシンボルがまだ残っています。したがって、エラーが報告されます。

そのため、通常、ライブラリはコマンド ラインの最後にリストする必要があります。

于 2012-07-06T11:25:40.073 に答える
4

ヘッダー ファイルがなければ、関数のプロトタイプはありません。これはエラーではありませんが、C コンパイラは、呼び出した関数のプロトタイプを想定します。

 int functionname()

空の () は「任意の引数」のようなものを意味するため、その関数に渡す任意の引数は、関数が呼び出される方法です。

引数の実際の型と関数の戻り値に応じて、それはうまくいくかもしれませんし、少なくとも状況によってはうまくいくように見えるかもしれません。

リンク段階では、実行可能ファイルを生成すると、リンカーは関数の名前でリンクするだけです。"foo" という名前の関数を呼び出し、ライブラリに "foo" という名前の関数シンボルもある場合、リンカーはそれを選択します。

于 2012-07-06T09:26:14.513 に答える
1

C では、プロトタイプなしで呼び出された関数は、次のようなプロトタイプを持つと見なされます。

int function();

これは、任意の数の任意の引数を受け入れ、int を返す関数を意味します。したがって、もちろん動作しますが、libcde の関数が想定していない引数を渡そうとすると、クラッシュします。

libelf.a をライブラリに入れることができます (オブジェクト ファイルをそこから自分のものにコピーするのではなく)。これはめったに良い考えではありません: ローカル システムの構成に依存するようになります (ライブラリがどこにあるかを見つける必要があります)。 )。

于 2012-07-06T09:28:16.553 に答える
0

C++ のヘッダーは、呼び出す関数のプロトタイプを提供するためにも使用されます。プロトタイプは、外部関数を呼び出すときにパラメーターのカウント/タイプを間違えないようにするために、コンパイラーによって使用されます。したがって、この時点では、静的ライブラリまたは動的ライブラリは何の違いもありません。

于 2012-07-06T09:22:03.337 に答える