1. ホーム
  2. c

[解決済み] C: リンカーコマンドが失敗し、終了コード 1 が表示されました。

2022-02-13 13:07:48

質問

コードをコンパイルするときに問題があります。エラーメッセージは出ていません。しかし、次のようなメッセージが表示されます。

Undefined symbols for architecture x86_64:
  "_lookup", referenced from:
      _main in sixteen2-85c27c.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

私は今までこのようなものに遭遇したことがなく、私がオンラインで見つけることができる情報のほとんどは、プレーンCではなく、Objective-Cに関係しています。

以下は私のコードです。

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[]) {
    struct entry {
        char word[15];
        char definition[100];
    };

    const struct entry dictionary[100] = 
    {{"aardvark", "a burrowing African mammal"},
    {"abyss", "a bottomless pit"},
    {"acumen", "mentally sharp; keen"},
    {"addle", "to become confused"},
    {"aerie", "a high nest"},
    {"affix", "to append; attach"},
    {"agar", "a jelly made from seaweed"},
    {"ahoy", "a nautical call of greeting"},
    {"aigrette", "an ornamental cluster of feathers"},
    {"ajar", "partially opened"}};

    int entries = 10;
    int entryNumber;
    int lookup (const struct entry dictionary[], const char search[], const int entries);

    if (argc != 2)
    {
        fprintf (stderr, "No word typed on the command line.\n");
        return EXIT_FAILURE;
    }

    entryNumber = lookup (dictionary, argv[1], entries);

    if (entryNumber != -1)
        printf("%s\n", dictionary[entryNumber].definition);
    else
        printf("Sorry, %s is not in my dictionary.\n", argv[1]);

    return EXIT_SUCCESS;
}

解決方法は?

次のようなコード行がありますね。

int lookup (const struct entry dictionary[], const char search[], const int entries);

main関数の内部です。 これには2つの大きな問題があります。

  1. 関数宣言は、他の関数の中に記述することはできません。 私が引用した行は、以下のようにmain関数の外側に表示する必要があります。

    int lookup(.....)
    //code here
    int main(...)
    {
        //more code here
    }
    
    
  2. 関数 "lookup" を宣言しているにもかかわらず、決して 定義 です。 これがリンカーエラーの原因になっていると思われます。 コンパイラがすべての関数をコンパイルし、リンカがその関数を(いわば)リンクしようとしたところ、関数 "lookup" が宣言され、後で呼び出されてはいるものの、実際にはどこにも存在しないことがわかったのです! (私の知る限り、関数 "lookup" は stdlib.h や stdio.h には存在しません). このため、リンカはその仕事をすることができません。

要約すると、あなたのコードは以下のように再編成されるはずです。

#include <stdio.h>
#include <stdlib.h>

int lookup (const struct entry dictionary[], const char search[], const int entries)
{
    //lots of code belongs in here, but that's for you to figure out :)
}

int main (int argc, char *argv[]) {
    struct entry {
        char word[15];
        char definition[100];
    };

    const struct entry dictionary[100] = 
    {{"aardvark", "a burrowing African mammal"},
    {"abyss", "a bottomless pit"},
    {"acumen", "mentally sharp; keen"},
    {"addle", "to become confused"},
    {"aerie", "a high nest"},
    {"affix", "to append; attach"},
    {"agar", "a jelly made from seaweed"},
    {"ahoy", "a nautical call of greeting"},
    {"aigrette", "an ornamental cluster of feathers"},
    {"ajar", "partially opened"}};

    int entries = 10;
    int entryNumber;
    //**REMOVE THIS LINE**  Move it up top....
    //int lookup (const struct entry dictionary[], const char search[], const int entries);

    if (argc != 2)
    {
        fprintf (stderr, "No word typed on the command line.\n");
        return EXIT_FAILURE;
    }

    entryNumber = lookup (dictionary, argv[1], entries);

    if (entryNumber != -1)
        printf("%s\n", dictionary[entryNumber].definition);
    else
        printf("Sorry, %s is not in my dictionary.\n", argv[1]);

    return EXIT_SUCCESS;
}

お役に立てれば幸いです。 もし、リンカーが何をするものなのか、なぜこれが重要なのか理解できない場合は、コメントを残していただければ、それに応じて私の答えを編集します。 リンカーとコンパイラーを理解することは、平凡なCプログラマーと偉大なCプログラマーを分ける多くの要因の1つです!


この際、リンカーとは何かということを説明するのも有効だと思います。 しかし、その前に、コンパイラが何をするのか、そして、コンパイルされたプログラムが本当は何なのかを理解することが重要なのです。

質問に投稿されたコードから、あなたはコンパイラーを複数回使用したことがあると推測してよいと思います :). あなたがすでに知っていることをいくつか言いますが、お付き合いください。 あなたが持っている ".c" と ".h" ファイルは、アルゴリズムを記述したものです。 これらのファイルは人間が読んだり編集したりするのは簡単ですが、コンピュータがそこから意味を抽出するのは難しいのです。 コンピュータがこれらのファイル内のプログラムを実行するためには、アルゴリズムをコンピュータが理解できる言語に変換する必要があります。 機械語」と呼ばれるこの言語は、人間が書くには難しいので、CやC++で書き、コンパイラーを使ってCやC++のプログラムを「機械語」に変換しているのです。

さて、ここでのリンカーの働きは微妙ですが、非常に重要です。 初心者は、リンカーは全く必要ないと思いがちです。コンパイラーがC言語を機械語に変換してくれれば、それで仕事は終わりですからね。

コンパイルされたプログラムの中の機械語には、メモリアドレスがあります。 次のようなおもちゃのような例を考えてみてください。

#include <stdio.h>

int addnums(int numbers[], int length)
{
    int total = 0;
    for(int i = 0; i < length; i++)
    {
        total += numbers[i];
    }
    
    return total;
}

int main(int argc, char** argv)
{
    int mynums[] = {1, 2, 3, 4, 5};
    int total = addnums(mynums, 5);
    printf("the total of the array is %i\n", total);
    return 0;
}

このおもちゃのプログラムの機械語コードを見てみると、各関数はコンピュータのメモリ上に始点と終点を持っていることがわかるだろう。 つまり、各関数には アドレス で始まる。 コンピュータがこの行を実行すると

int total = addnums(mynums, 5);

int main()" はアドレス100で始まり、int addnums()" はアドレス50にあるとします。 コンピュータは、この行に到達したら、アドレス50のマシン・コードを実行する必要があります。

コンパイラは関数のアドレスは気にしません。 ただコンパイルするだけで、正しいアドレスをマシンコードに書き込むのは別のプログラムに任されています。 もしリンカが次のようなことを言ったら

symbol(s) not found

は、リンカがある関数のアドレスを知らないことを意味します。 これは通常、問題の関数が一度も宣言または定義されていないことが原因です。 このコードから実行ファイルを作成すると、コンピューターは次のような行に到達します。

int total = addnums(mynums, 5);

int addnums() "関数にジャンプするアドレスがなければ、あなたのCPUは完全に迷子になってしまうでしょう。

を実行すると、プログラムが不思議なことにクラッシュしてしまいます。

リンカはエラーを出してコンパイルを止めるので、この心労を軽減することができます。 素人時代に組み込みシステムのプログラミングをしていたとき、この問題に何度か遭遇し、デバッグするのが悪夢のようでした。

お役に立てれば幸いです。 もし私の説明が悪かったら教えてください。