gcc编译出现 undefined reference to 的问题

来源:互联网 发布:php array remove key 编辑:程序博客网 时间:2024/05/16 09:44
今天去腾迅面试,虽然失败了,不过有些问题还是总结一下。
之前在用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 $(TARGETtest.afunc.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编译的默认规则。
0 0
原创粉丝点击