連結時未定義的引用錯誤

編譯中最常見的錯誤之一發生在連結階段。該錯誤看起來類似於:

$ gcc undefined_reference.c 
/tmp/ccoXhwF0.o: In function `main':
undefined_reference.c:(.text+0x15): undefined reference to `foo'
collect2: error: ld returned 1 exit status
$

那麼讓我們看一下生成此錯誤的程式碼:

int foo(void);

int main(int argc, char **argv)
{
    int foo_val;
    foo_val = foo();
    return foo_val;
}

我們在這裡看到 foo(int foo();) 的宣告,但沒有它的定義 (實際函式)。所以我們為編譯器提供了函式頭,但是在任何地方都沒有定義這樣的函式,所以編譯階段通過但連結器以 Undefined reference 錯誤退出。
要在我們的小程式中修復此錯誤,我們只需要為 foo 新增一個定義

/* Declaration of foo */
int foo(void);

/* Definition of foo */
int foo(void)
{
    return 5;
}

int main(int argc, char **argv)
{
    int foo_val;
    foo_val = foo();
    return foo_val;
}

現在這段程式碼將編譯。另一種情況出現在 foo() 的源位於一個單獨的原始檔 foo.c 中(並且有一個標頭檔案 foo.h 來宣告 foo() 包含在 foo.cundefined_reference.c 中)。然後修復是連結 foo.cundefined_reference.c 的目標檔案,或者編譯兩個原始檔:

$ gcc -c undefined_reference.c 
$ gcc -c foo.c
$ gcc -o working_program undefined_reference.o foo.o
$

要麼:

$ gcc -o working_program undefined_reference.c foo.c
$

更復雜的情況是涉及到庫,例如程式碼:

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

int main(int argc, char **argv)
{
    double first;
    double second;
    double power;

    if (argc != 3)
    {
        fprintf(stderr, "Usage: %s <denom> <nom>\n", argv[0]);
        return EXIT_FAILURE;
    }

    /* Translate user input to numbers, extra error checking
     * should be done here. */
    first = strtod(argv[1], NULL);
    second = strtod(argv[2], NULL);

    /* Use function pow() from libm - this will cause a linkage 
     * error unless this code is compiled against libm! */
    power = pow(first, second);

    printf("%f to the power of %f = %f\n", first, second, power);

    return EXIT_SUCCESS;
}

程式碼在語法上是正確的,pow() 的宣告存在於 #include <math.h>,所以我們嘗試編譯和連結但是得到如下錯誤:

$ gcc no_library_in_link.c -o no_library_in_link
/tmp/ccduQQqA.o: In function `main':
no_library_in_link.c:(.text+0x8b): undefined reference to `pow'
collect2: error: ld returned 1 exit status
$

這是因為在連結階段沒有找到 pow()定義。要解決這個問題,我們必須通過指定 -lm 標誌來指定我們想要連結到名為 libm 的數學庫。 (請注意,有些平臺,如 macOS,不需要 -lm,但是當你得到未定義的引用時,需要庫。)

所以我們再次執行編譯階段,這次指定庫(在原始檔或目標檔案之後):

$ gcc no_library_in_link.c -lm -o library_in_link_cmd
$ ./library_in_link_cmd 2 4
2.000000 to the power of 4.000000 = 16.000000
$

它的工作原理!