1. ホーム

c c++ "undefined reference to "問題の解決方法

2022-04-05 13:28:47

 最近、Linuxでプログラミングをしていて、スタティック・ライブラリのリンク時に必ず以下のようなエラーが出るという奇妙な現象を発見しました。

(.text+0x13): `func' への未定義の参照 

    ここでは、よくあるエラーの原因と解決策を詳しく例示していますので、初心者の方の参考になればと思います。

1. リンク時にターゲットファイル(.o)が存在しない

テスト コードは以下の通りです。

    次にコンパイルします。

gcc -c test.c  
gcc -c main.c 

    main.oとtest.oの2つの.oファイルを取得し、.oをリンクして実行ファイルを取得します。

gcc -o main main.o 

    この時点で、次のようなエラーが報告されていることに気がつきます。

main.o: In function `main':  
main.c:(.text+0x7): undefined reference to `test'  
collect2: ld returned 1 exit status 

    これは最も典型的な未定義参照エラーで、リンク時に関数の実装が見つからないためです。この場合、test.o ファイルに test() 関数の実装が含まれているので、この方法でリンクすれば問題ありません。

gcc -o main main.o test.o 

   次のコンパイルでも未定義参照エラーが報告されますが、実は根本的な理由は上記と同じです。

gcc -o main main.c //missing test() implementation file 

test()関数の実装ファイルを一緒にコンパイルして、成功させるためには以下のような形に変更する必要があります。

gcc -o main main.c test.c //ok, no more problems 

2. リンク時に関連するライブラリファイル(.a/.so)が見つからない

    ここでは、以下のソースコードを想定して、スタティック・ライブラリの例だけを紹介します。

    まず、test.c をスタティック・ライブラリ (.a) ファイルにコンパイルします。

gcc -c test.c  
ar -rc test.a test.o 

    この時点で、test.a ファイルができました。それでは、main.cのコンパイルを始めましょう。

gcc -c main.c 

    この時点でmain.oファイルが生成されているので、実行形式を期待して以下のコマンドでリンクします。

gcc -o main main.o 

    コンパイラがエラーを報告していることに気がつくでしょう。

/tmp/ccCPA13l.o: In function `main':  
main.c:(.text+0x7): undefined reference to `test'  
collect2: ld returned 1 exit status 

    根本的な原因は、test()関数の実装ファイルが見つからないことです。test()関数の実装は静的ライブラリtest.aにあるので、リンク時にライブラリtest.aを後ろに追加し、リンクコマンドを以下のような形に修正する必要があります。

gcc -o main main.o . /test.a //Note: . / is the path to test.a 

     [エクステンション】です。] 繰り返しますが、物事を明確にするために、上記ではコードのコンパイル・リンクを分離しました。もし、実行ファイルを一度に生成したい場合は、main.c と test.a に対して以下のコマンドを実行すればよいでしょう。

gcc -o main main.c . /test.a //also, if you don't add test.a it will also report an error 

3. リンク先のライブラリファイルで別のライブラリファイルが使用されている

    これはもっと陰湿な問題で、ネットで話題になっているのとは違う、私が最近遭遇した問題なので、その例を紹介します。

    上の画像からわかるように、main.c は test.c の関数を呼び出し、その関数が fun.c の関数を呼び出しています。



    まず、fun.c, test.c, main.cをコンパイルして、.oファイルを生成します。

gcc -c func.c  
gcc -c test.c  
gcc -c main.c 

    そして、test.cとfunc.cはそれぞれ静的ライブラリファイルとしてパッケージ化されています。

ar -rc func.a func.o  
ar -rc test.a test.o 

    この時点で、main.o を実行ファイルとしてリンクする準備ができました。main.c には test() の呼び出しが含まれているので、次のリンクコマンドでライブラリファイルとして test.a とリンクする必要があります。

gcc -o main main.o test.a 

    この時点でも、コンパイラは次のようにエラーを報告しています。

test.a(test.o): In function `test':  
test.c:(.text+0x13): undefined reference to `func'  
collect2: ld returned 1 exit status 

    つまり、リンクする際に、私たちのtest.aはfunc()関数を呼び出し、対応する実装を見つけることができなかったことがわかったのです。このことから、正常にリンクするためには、test.aが参照しているライブラリファイルも追加する必要があることがわかりましたので、以下のようなコマンドを実行します。

gcc -o main main.o test.a func.a 

    OK、これで無事に最終的なプログラムを得ることができます。同様に、もし私たちのライブラリやプログラムがサードパーティのライブラリ(例えばpthread.a)を参照している場合、リンク時にサードパーティのライブラリへのパスとライブラリファイルも与える必要があり、さもなければ未定義の参照エラーが発生します。

4 複数のライブラリファイルのリンク順

    この問題も非常に陰湿で、調べないと非常に困惑することになります。サブセクション3で説明した問題に戻ってみましょう。最後に、リンクされているライブラリの順番を変えるとどうなるでしょうか?

gcc -o main main.o func.a test.a 

    以下のようなエラーが発生します。

test.a(test.o): In function `test':  
test.c:(.text+0x13): undefined reference to `func'  
collect2: ld returned 1 exit status 

    したがって、リンクコマンドで依存ライブラリを指定する際には、ライブラリ間の依存関係の順序に注意する必要があり、他のライブラリに依存しているライブラリは、依存ライブラリよりも前に置くことで、未定義の参照エラーを本当に回避して、コンパイルとリンクを完了させることができるのです。

5. c++コードでのリンク C言語 のライブラリを使用します。

    また、ライブラリファイルがcコードから生成されている場合、c++のコードでライブラリ内の関数をリンクすると、未定義参照の問題に遭遇することになります。以下はその例です。

    まず、c版のライブラリファイルを書きます。 

    コンパイル、静的ライブラリとしてパッケージ化: test.a


  1. gcc -c test.c  
  2. ar -rc test.a test.o 

    この時点で、test.a ファイルができました。次に、c++ファイルmain.cppを書き始めます。

    その後、main.cppをコンパイルして、実行ファイルを生成します。

g++ -o main main.cpp test.a

    は、次のように報告されたエラーを見つけます。

/tmp/ccJjiCoS.o: In function `main': 
main.cpp:(.text+0x7): undefined reference to `test()' 
collect2: ld returned 1 exit status 

    理由は、main.cppは、c言語ライブラリの関数を呼び出すc + +コードなので、リンクが見つかりません、解決策:つまり、main.cppで、c言語ライブラリtest.aに関連するヘッダーファイルはextern "C" 宣言を追加できます含んでいます。たとえば、変更されたmain.cppは、次のようになります。

g++ -o main main.cpp test.a 

    再度コンパイルすると、問題が正常に解決されたことがわかります。

上記