今天去腾迅面试,虽然失败了,不过有些问题还是总结一下。
之前在用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
后来看了这个链接才发现问题:
先看一下标准的示例吧
这个链接里面已经说得很清楚了,就拿一个比较容易的例子说明:
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 问题
什么是编译,链接
参看这个说明
简单翻译一下,编译就是把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编译的默认规则。
阅读(10525) | 评论(0) | 转发(0) |