链接时未定义的引用错误

编译中最常见的错误之一发生在链接阶段。该错误看起来类似于:

$ 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
$

它的工作原理!