一直以来对Linux下编译链接产生的问题没有好好重视起来,出现问题就度娘一下,很多时候的确是在搜索帮助下解决了BUG,但由于对原因不求甚解,没有细细研究,结果总是在遇到在BUG时弄得手忙脚乱得。
甚至有时候为了一个问题查了半天的资料,好不容易解决了,却因为没有记录下来或者没有弄清楚真实原因,结果第二次碰到还是要去重复前次的折腾,很是尴尬无奈。
虽然,同样的错误信息,其产生的原因不一而足,但是,总结一下终归是好的,使不知变知之,只要不在同一件事情上重复同样的错误,发现的问题越多,解决的问题越多,未知的问题也就越少,不是吗?
针对Linux下编译链接的问题,网络上不乏有牛人经典的原创好文,不得不说,现在网络的发达让人们:不足出户,就可以万事不求人。废话少说,言归正传罢:
1, 现在先看一个最简单的例子,那就是我们经常在编程语言入门时最常见的"Hello World!"例子:
<main.cpp>
#include <stdio.h> int main(int argc, char *agv[]) { printf("Hello World\n"); return 0; }
编译这个程序我们最简单的方法就是:g++ main.cpp;然后编译器便会对源文件进行编译链接,最后生成默认的a.out执行文件。期间并不会出现任何错误提示。
对比一下当我们使用第3方软件库时,要添加一些必要的编译参数(比如说:-I库头文件;-l库名;-L库路径),才能让编译器正常工作而不出现错误提示。
个中原因就在于:前者是标准库,已经被做成系统软件库的一分子,编译器可以直接在系统上拿过来使用,所以从表面上看会省略掉一些参数和细节。
其实不管是使用系统标准库,还是使用第3方软件生成库,抑或是我们自己软件生成库,编译器在进行编译链接时,除了库名和路径不同外,他们的编译链接原理都是相同相通的。
2,编译和链接
语法上的错误比较基础,我们不做讨论;
"#include <头文件名>" 和 "-I<目录>" :解决的是编译问题,前者是头文件名,后者是头文件所在目录
"-l<库名>" 和 "-L<目录>" :是解决的链接问题, 前者是库名,后者是库名所在目录
下面是main ->main.o -> test.o的例子,
各文件源代码如下:
test.h
//test.h extern void test();
test.cpp
//test.cpp #include <stdio.h>#include <test.h> void test() { printf("Hello World!\n"); }
main.cpp
//main.cpp #include "test.h" int main(void) { test(); return 0; }
正确编译步骤:
[[email protected] link]# g++ -o main main.cpp test.cpp
现在我们对这个例子做一下变形:
检查编译链接参数,看看有没有把接口所在的库文件包含进来。
2.1 编译选项在链接时缺少库文件(.o/.so/.a)
[[email protected] link]# g++ -o main main.cpp
main.o: In function `main‘:
main.cpp:(.text+0x5): undefined reference to `test()‘
collect2: ld returned 1 exit status
[[email protected] link]# g++ -o main test.cpp
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crt1.o: In function `_start‘:
(.text+0x20): undefined reference to `main‘
collect2: ld returned 1 exit status
1, 编译选项中,缺少库文件(.o/.so/.a),
如: g++ main test.cpp <main.cpp>
g++ main -l<lib1> -l<lib2>
2,编译选项中,多个依赖库的顺序不对,尤其是对静态库来说,顺序是很讲究的。
如: main->test->func;那么g++ main main.cpp func.a test.a 将会报出
[[email protected] link]# g++ -o main main.o func.a test.a
test.a(test.o): In function `test()‘:
test.cpp:(.text+0x5): undefined reference to `func()‘
collect2: ld returned 1 exit status
附上静态库生成命令:
g++ -c test.cpp -o test.o
g++ -c func.cpp -o func.o
ar -rcs func.a func.o
ar -rcs test.a test.o
2,函数接口未实现
/tmp/ccE1htXD.o: In function `main‘:
main.cpp:(.text+0x5): undefined reference to `test()‘
collect2: ld returned 1 exit status
A: test.h void test();
test.cpp: void Test() {} 或 void test(int n) {} 或 int test() {}
3,函数接口实现了,但是
3.1 调用接口命名重编或调用约定不一致引起的接口导出不匹配,比如说跨语言调用接口(如C++调用C生成库的接口)时。