您的位置:首页 > Web前端

gcc编译出现 undefined reference to 的问题

2014-08-13 17:39 579 查看
今天去腾迅面试,虽然失败了,不过有些问题还是总结一下。

之前在用GCC编译Mysql的代码时发现一个奇怪的问题,后来调整了一下参数的顺序就好了,也没有想原因。

代码很简单:

点击(此处)折叠或打开

#include <mysql/my_global.h>

#include <mysql/mysql.h>

int main(int argc, char
**argv)

{

    printf("MySQL client version: %s\n", mysql_get_client_info());

    exit(0);

}

对应的Makefile文件:

点击(此处)折叠或打开

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

main:$(Object)
$(CC) $(CFLAGS) -o $(TARGET) $(Object)
clean:
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

点击(此处)折叠或打开

#include <stdio.h>

#include "test.h"

int main()
{

    test();

    return 0;

}

test.c

点击(此处)折叠或打开

#include "test.h"

#include "func.h"

void test()

{

    func();

}

func.c

点击(此处)折叠或打开

#include "func.h"
 

void func()

{

}

这里面的调用关系为:main()->test()->func()

将test.c func.c 两个文件编译成静态库:
Makefile

点击(此处)折叠或打开

TARGET=main

main:test.a func.a main.o
gcc main.o -o $(TARGET)
func.a test.a

func.a:func.o
ar -rc func.a func.o
test.a:test.o
ar -rc test.a test.o
main.o:main.c
gcc -c main.c
test.o:test.c
gcc -c test.c
func.o:func.c
gcc -c func.c
clean:
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.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编译的默认规则。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐