gcc编译undefined reference to本质原因

本文深入探讨了GCC编译器的工作流程,包括预处理、编译、汇编及链接阶段,并通过实例演示了如何诊断并解决未定义符号错误。同时,文章还介绍了头文件的用途及其在大型项目中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目中的LVS用到keepalived和ipvsadm等三方件,在suse11和suse12上编译最新版本的过程中遇到的最多的错误便是 undefined reference to xxx。由于对背后的原理基本没啥理解,所以遇到问题的解决办法就是把错误信息拿去google,baidu搜。当遇到的问题越来越偏僻时,这种做法不仅学不到多少东西,也无法快速的解决问题。所以下定决心从头学起,刚好对此也非常感兴趣,学习这些知识对学习操作系统一定非常有帮助。

动手实践之前,先来一点理论知识的指导:
gcc程序的编译过程和链接原理

为了加深印象与理解,按自己的理解总结一下,gcc的编译选项:

  • -o 指定输出文件名为file,这个名称不能跟源文件名同名。不指定时,-E会输出到标准输出(一般为屏幕)。-S输出到file.s。-c 输出到file.o。不使用以上选项时,输出到a.out。
  • -E Preprocess only; do not compile, assemble or link;只预处理
  • -S Compile only; do not assemble or link;只编译(得到的是汇编代码,mov,push这种
  • -c Compile and assemble, but do not link; 编译和汇编。得到的是二进制。我理解此时的二进制已经和操作系统的具体指令相关了。
  • 不指定参数,默认进行编译,汇编,链接。得到是可执行二进制。

了解了原理,接下来进行实践加深理解。
参考:
Linux makefile – undefined reference to 问题解决方法

测试代码如下:

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>

int main()
{
        test();
}
copbint@debian2:~/workspace/test1$ cat test.c
#include <stdio.h>

void test()
{
        printf("I am test!\n");
}

直接进行编译:

copbint@debian2:~/workspace/test1$ gcc -o main main.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
  test();
  ^~~~
/tmp/ccXDYxeL.o: In function `main':
main.c:(.text+0x1c): undefined reference to `test'
collect2: error: ld returned 1 exit status

加上test.c便正确:

copbint@debian2:~/workspace/test1$ gcc -o main main.c test.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
  test();
  ^~~~

虽然能够链接成功,但是还是会有警告。经过测试,原来是在编译的过程中,发出了警告。

copbint@debian2:~/workspace/test1$ gcc -S  main.c test.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
  test();
  ^~~~

由此可以理解,在编译的过程中,如果找不到函数的实现,只会抛出implicit declaration of的警告。如果在链接的过程中,找不到函数的实现,则会导致错误。
在main.c中加入test()的声明,再实验如下:

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
void test();
int main()
{
        test();
}
copbint@debian2:~/workspace/test1$ cat test.c
#include <stdio.h>

void test()
{
        printf("I am test!\n");
}
copbint@debian2:~/workspace/test1$ gcc -c main.c
copbint@debian2:~/workspace/test1$ gcc -o main  main.c
/tmp/ccISqKfs.o: In function `main':
main.c:(.text+0x1c): undefined reference to `test'
collect2: error: ld returned 1 exit status

由此可以得出结论:
undefined reference to xxx是由于gcc在链接的过程中找不到函数的实现而导致的错误。如果是找不到函数的声明,会在编译的过程出抛出警告。

那么,如果在代码中使用未定义的变量,在编译(不链接)的过程中是否像未定义的函数一样,仅仅是抛出警告而不会导致错误呢?

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
int main()
{
        test_h_var = 3;
}
copbint@debian2:~/workspace/test1$ gcc -c main.c
main.c: In function ‘main’:
main.c:5:2: error: ‘test_h_var’ undeclared (first use in this function)
  test_h_var = 3;
  ^~~~~~~~~~
main.c:5:2: note: each undeclared identifier is reported only once for each function it appears  in

在编译的过程中,与使用未定义的函数不同,使用未定义的变量会直接导致错误。

在稍大一点的软件项目中,如果用到一个函数,就手动的去声明一次,会有工作量大且难以维护的问题。头文件就是为了解决这个问题,如为test.c添加一个test.h。然后在main.c中引用。

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
#include "test.h"
int main()
{
        test();
}
copbint@debian2:~/workspace/test1$ cat test.h
void test();

使用<>括起来的头文件,gcc会去系统路径下查找(具体路径还不了解),而”“括起来的头文件,gcc会尝试在当前目录搜索。

头文件除了声明函数,还有定义变量,宏,结构体等的作用。所以,在实际应用过程中,头文件必不可少。


在suse11上编译keepalived1.3.5的过程中,遇到大量 undefined reference to xxx错误,但是却并未看到implicit declaration of的告警信息。说明是在链接的过程中未找到对应函数的实现体。那么什么原因会导致这种情况呢?

未完待续……

### GCC 编译时出现未定义引用错误的解决方案 当遇到 `undefined reference` 错误时,这通常意味着链接器无法找到某些函数或变量的实际定义。对于像 `pthread_create` 和 `pthread_join` 这样的 POSIX 线程库函数,在编译过程中需要显式地告诉链接器去查找这些符号。 #### 对于 pthread 库 如果代码中有如下包含语句: ```c #include <pthread.h> ``` 但在编译时遇到了 `undefined reference to 'pthread_create'` 或者类似的错误,则应该确保在编译命令最后加上 `-lpthread` 参数来指定链接线程库[^1]。完整的编译指令应类似于下面这样: ```bash gcc your_program.c -o output_executable -lpthread ``` 这里 `-lpthread` 告诉链接器要链接到 pthread 库中寻找所需的符号定义。 #### 关于 undefined reference本质原因 此类错误的根本原因是链接阶段未能成功定位所需外部资源的具体位置。值得注意的是,这类问题是发生在链接期而非预处理或者编译期间;因此即使头文件被正确引入也不会影响该类问题的发生[^2]。 #### 针对 math 函数的情况 对于数学运算函数如 sqrt() ,虽然包含了 `<math.h>` 头文件,但如果使用 gcc 而不是 g++ 来编译含有此类函数调用的应用程序,则同样可能会遭遇 `undefined reference` 报错。这是因为 gcc 默认情况下并不会自动连接 C 标准库之外的标准数学库 (libm),所以还需要额外添加 `-lm` 选项让链接器知道去哪里找寻相应的实现[^3]: ```bash gcc program_with_math_functions.c -o executable_name -lm ``` 而对于 inet_pton 函数而言,在 Windows 平台上通过 MinGW 使用 gcc 编译时,除了包含必要的网络编程头文件外,还需特别指明依赖 winsock2 库(`-lws2_32`)以解决相应未定义引用的问题[^4] : ```bash gcc test.c -o test.exe -lws2_32 ``` 总之,面对不同类型的 `undefined reference` 错误,关键是确认所使用的具体库,并且按照上述指导调整编译参数以便正确链接那些第三方或是平台特定的功能模块。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值