gcc编译出现 undefined reference to 的问题

本文通过实例解析了GCC编译时参数顺序对链接过程的影响,特别是对于静态库和动态库的依赖顺序,揭示了正确的参数排列方式以避免未定义符号错误。

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

今天去腾迅面试,虽然失败了,不过有些问题还是总结一下。
之前在用GCC编译Mysql的代码时发现一个奇怪的问题,后来调整了一下参数的顺序就好了,也没有想原因。
代码很简单:

点击(此处)折叠或打开

  1. #include <mysql/my_global.h>
  2. #include <mysql/mysql.h>

  3. int main(int argc, char **argv)
  4. {
  5.     printf("MySQL client version: %s\n", mysql_get_client_info());

  6.     exit(0);
  7. }
对应的Makefile文件:

点击(此处)折叠或打开

  1. TARGET=main
  2. CFLAGS=-Wall $(shell mysql_config --cflags --libs)
  3. HEADER=$(mysql_config --cflags)
  4. CC=gcc
  5. Object=progname.c

  6. main:$(Object)
  7. $(CC) $(CFLAGS) -o $(TARGET) $(Object)
  8. clean:
  9. rm -f $(TARGET)
关键是这句: $(CC) $(CFLAGS) -o $(TARGET) $(Object),CFLAGS 放到了Object 的前面,结果编译的时候出现:
gcc -Wall -I/usr/include/mysql -DBIG_JOINS=1  -fno-strict-aliasing  -g -L/usr/lib/x86_64-linux-gnu -lmysqlclient -lpthread -lz -lm -lrt -ldl -o main progname.c
/tmp/ccSTALlC.o: In function `main':
/home/ufisher/Documents/tmp/mysql/progname.c:6: undefined reference to `mysql_get_client_info'
collect2: ld returned 1 exit status
make: *** [main] Error 1

如果把这句改成 $(CC)  -o $(TARGET) $(Object) $(CFLAGS), 结果就对了:
gcc  -o main progname.c -Wall -I/usr/include/mysql -DBIG_JOINS=1  -fno-strict-aliasing  -g -L/usr/lib/x86_64-linux-gnu -lmysqlclient -lpthread -lz -lm -lrt -ldl

后来看了这个链接才发现问题: http://hi.baidu.com/whiteprincer/item/d5c4e8e339d134d5eb34c9f0

先看一下标准的示例吧

这个链接里面已经说得很清楚了,就拿一个比较容易的例子说明:
main.c

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include "test.h"

  3. int main() {
  4.     test();
  5.     return 0;
  6. }
test.c

点击(此处)折叠或打开

  1. #include "test.h"
  2. #include "func.h"

  3. void test()
  4. {
  5.     func();
  6. }
func.c

点击(此处)折叠或打开

  1. #include "func.h"
  2.  
  3. void func()
  4. {
  5. }
这里面的调用关系为:main()->test()->func()
将test.c func.c 两个文件编译成静态库:
Makefile

点击(此处)折叠或打开

  1. TARGET=main

  2. main:test.a func.a main.o
  3. gcc main.o -o $(TARGET) func.a test.a
  4. func.a:func.o
  5. ar -rc func.a func.o
  6. test.a:test.o
  7. ar -rc test.a test.o
  8. main.o:main.c
  9. gcc -c main.c
  10. test.o:test.c
  11. gcc -c test.c
  12. func.o:func.c
  13. gcc -c func.c
  14. clean:
  15. rm -f $(TARGET) *.o *.a
注意第四句的静态库的顺序,这样执行Make的时候同样会出现 undefined reference to 问题

什么是编译,链接

参看这个说明 http://www.cprogramming.com/compilingandlinking.html
简单翻译一下,编译就是把C/C++的高级语言翻译成机器语言指令,编译过程是对独立的文件,并不检查函数的定义放在什么地方,也不会生成可以执行的文件,通常是生成.o(.obj)这样的文件。
而链接则要根据.o文件生成可执行的程序或库。函数未定义这样的错误都是在链接过程中产生的。编译过程如果找不到一个函数的定义,它会认为这个函数的定义放在其它文件,而链接则一定要找到第个函数的定义。
这样多个库文件在链接时就有了依赖性的问题。

再看上面的问题,由于是从.o文件生成了静态库,最后两个静态库的顺序是func.a  test.a, 当链接func.a时找到func函数,它并不依赖于其它的库,但链接到test.a时找到test函数,它依赖于func,结果出现了,
从这里可以判断Gcc的链接顺序是 被依赖的库放在其它库的后面,比如test依赖func,则func.a应该放到test.a的后面。
因此把上面Makefile中的第4行改成 gcc main.o -o $(TARGET test.a func.a 就没有问题了。

再来看 Mysql那个示例的问题,展开的的编译命令是这样的:
gcc  -o main progname.c -Wall -I/usr/include/mysql -DBIG_JOINS=1  -fno-strict-aliasing  -g -L/usr/lib/x86_64-linux-gnu -lmysqlclient -lpthread -lz -lm -lrt -ldl
在链接的时候 progname里面用到了动态库的函数,所以引入动态库的参数应该放在后面,反之如果放在前面,progname中的mysql_get_client_info() 就会找不到定义。

当然上面说的顺序问题都是存在于库中,包括动态库和静态库,如果把所有文件都编译成.o文件,然后直接链接成最终的程序,也不会有问题。
但对于Mysql这样的示例,有时候就需要外部的库,因此还是需要了解gcc编译的默认规则。
### 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` 错误,关键是确认所使用的具体库,并且按照上述指导调整编译参数以便正确链接那些第三方或是平台特定的功能模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值