代码文件初始化
a.h
#ifndef A_H
#define A_H
#include <iostream>
class A
{
public:
A(){}
~A(){}
void hello();
};
#endif
a.cpp
#include "a.h"
void A::hello()
{
std::cout << "hello world\n";
}
b.h
#ifndef B_H
#define B_H
#include "a.h"
#include <iostream>
class B
{
public:
void sayB();
private:
A a;
};
#endif
b.cpp
#include "b.h"
void B::sayB()
{
std::cout << "B is running\n";
a.hello();
std::cout << "B is end\n";
}
c.cpp
#include "b.h"
int main()
{
std::cout << "C is running\n";
B b;
b.sayB();
std::cout << "C is end\n";
}
.o文件编译可执行文件
目标文件(.o文件)编译,不讲究顺序
生成各个文件的.o文件
[root@localhost test]# g++ -c *.cpp
[root@localhost test]# g++ -o main a.o b.o c.o
[root@localhost test]# g++ -o main c.o b.o a.o
[root@localhost test]# g++ -o main *.o
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
直接g++ c.cpp -o main是不行的,会缺依赖,少了B::sayB()的实现
[root@localhost test]# g++ c.cpp -o main
/tmp/cc2F6BQH.o: In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status
直接g++ c.cpp -o main b.o也是不行的,会缺依赖,少了A::hello()的实现
[root@localhost test]# g++ c.cpp -o main b.o
b.o: In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
正确
[root@localhost test]# g++ c.cpp -o main b.o a.o
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
创建静态链接文件并编译
使用 ar 将目标文件归档
ar crv libabc.a a.o b.o c.o
其中:
- c 如果需要生成新的库文件,不要警告
- r 代替库中现有的文件或者插入新的文件
- v 输出详细信息
通过 ar t libabc.a
可以查看 libabc.a
中包含的目标文件,还可以通过 ar --help
查看更多帮助。
注意:我们要生成的库的文件名必须形如 libxxx.a
,这样我们在链接这个库时,就可以用 -lxxx
。反过来讲,当我们告诉编译器 -lxxx
时,编译器就会在指定的目录中搜索 libxxx.a
或是 libxxx.so
。
静态链接文件:在链接阶段,将源文件中用到的库函数与汇编生成的目标文件.o合并生成可执行文件。
静态链接文件(.a文件)链接,讲究顺序,左边的lib库依赖右边的lib库
不讲究顺序的错误方式:
[root@localhost test]# g++ -o main aaa/liba.a aaa/libb.a aaa/libc.a
aaa/libc.a(c.o): In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main aaa/*.a
aaa/libc.a(c.o): In function `main':
c.cpp:(.text+0x2c): undefined reference to `B::sayB()'
collect2: error: ld returned 1 exit status
1、使用3个.a静态链接文件三种方式
(1)正确顺序的依赖
libc.a依赖libb.a,libb.a依赖liba.a
[root@localhost test]# g++ -o main aaa/libc.a aaa/libb.a aaa/liba.a
(2)多次依赖
在使用一些循环依赖关系比较复杂的静态库时,也可以在链接序列中,让一个静态库出现多次,来解决一些循环依赖。
[root@localhost test]# g++ -o main aaa/liba.a aaa/libb.a aaa/libc.a aaa/libb.a aaa/liba.a
(3)使用Xlinker
Xlinker选项是将参数传给链接器,链接器在处理”-(”和”-)”之间的静态库时,是会重复查找这些静态库的,所以就解决了静态库查找顺序问题。不过,这种方式比人工提供链接顺序的方式效率会低很多。
[root@localhost test]# g++ -o main -Xlinker "-(" aaa/liba.a aaa/libb.a aaa/libc.a -Xlinker "-)"
2、使用2个.a静态链接文件
少了liba
[root@localhost test]# g++ c.cpp -o main aaa/libb.a
aaa/libb.a(b.o): In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
链接顺序出错
[root@localhost test]# g++ c.cpp -o main aaa/liba.a aaa/libb.a
aaa/libb.a(b.o): In function `B::sayB()':
b.cpp:(.text+0x23): undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
正确
[root@localhost test]# g++ c.cpp -o main aaa/libb.a aaa/liba.a
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
3、将a.cpp和b.cpp制作成.a,然后编译c.cpp时进行链接
[root@localhost test]# ar crv aaa/libab.a a.o b.o
[root@localhost test]# g++ c.cpp -o main -L./aaa -lab
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
4、将a.cpp,b.cpp和c.cpp制作成.a,然后直接生成可执行文件时链接
[root@localhost test]# ar crv aaa/libabc.a a.o b.o c.o
[root@localhost test]# g++ -o main -L./aaa -labc
[root@localhost test]# ./main
C is running
B is running
hello world
B is end
C is end
5、分别制作三个.a,分别链接,发现不行,原因未知
[root@localhost test]# g++ -o main -L./aaa/tmp -la -lb -lc
/usr/bin/ld: ./aaa/tmp/libc.a(c.o): undefined reference to symbol '__cxa_atexit@@GLIBC_2.2.5'
/usr/bin/ld: note: '__cxa_atexit@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main -L./aaa/tmp -lc -lb -la
/usr/bin/ld: ./aaa/tmp/libc.a(c.o): undefined reference to symbol '__cxa_atexit@@GLIBC_2.2.5'
/usr/bin/ld: note: '__cxa_atexit@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
创建动态链接文件并编译
编译生成动态库
g++ -fPIC -shared -o ./soso/libab.so a.cpp b.cpp
实际上上述过程分为编译和链接两步, -fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性; -shared是链接选项,告诉g++生成动态库而不是可执行文件。
上述的一行命令等同于:
g++ -c -fPIC a.cpp b.cpp # 注意必须带-fPIC,不带的话生成的.o目标文件是没法用于生成.so文件的
g++ -shared -o libab.so a.o b.o
g++ c.cpp -o main -L./soso -lab
生成main,其中-lab
表示要链接libab.so
。
-L./soso
表示搜索要链接的库文件时包含./soso
路径。
如果同一目录下同时存在同名的动态库和静态库,比如 libab.so
和 libab.a
都在当前路径下,g++会优先链接动态库。想链接静态库需要显式指定g++ -static c.cpp -o main -L. -lab
动态链接文件:在程序运行过程中动态调用库文件,很方便,不占空间
动态链接文件(.so文件)链接,不讲究顺序
1、使用3个so动态链接文件
[root@localhost test]# g++ -o main soso/liba.so soso/libb.so soso/libc.so
[root@localhost test]# g++ -o main soso/libc.so soso/libb.so soso/liba.so
[root@localhost test]# g++ -o main soso/*.so
2、使用2个so动态链接文件
[root@localhost test]# g++ c.cpp -o main soso/libb.so
soso/libb.so: undefined reference to `A::hello()'
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ c.cpp -o main soso/libb.so soso/liba.so
3、将a.cpp和b.cpp制作成so,然后编译c.cpp时进行链接
[root@localhost test]# g++ -fPIC -shared -o ./soso/libab.so a.cpp b.cpp
[root@localhost test]# g++ c.cpp -o main -L./soso -lab
[root@localhost test]# ./main
./main: error while loading shared libraries: libab.so: cannot open shared object file: No such file or directory
找不到libab.so,因为Linux是通过 /etc/ld.so.cache 文件搜寻要链接的动态库的, /etc/ld.so.cache 是 ldconfig 程序读取 /etc/ld.so.conf 文件生成的(/etc/ld.so.conf 中并不一定需要包含 /lib 和 /usr/lib,ldconfig程序会自动搜索这两个目录)。
如果我们把 libab.so 所在的路径添加到 /etc/ld.so.conf 中,再以root权限运行 ldconfig 程序,更新 /etc/ld.so.cache ,main运行时,就可以找到 libab.so。
也可以直接用命令LD_LIBRARY_PATH=./soso的方式指定动态链接库路径。
[root@localhost test]# LD_LIBRARY_PATH=./soso ./main
C is running
B is running
hello world
B is end
C is end
4、将a.cpp,b.cpp和c.cpp制作成so,然后直接生成可执行文件时链接
[root@localhost test]# g++ -fPIC -shared -o ./soso/libabc.so a.cpp b.cpp c.cpp
[root@localhost test]# g++ -o main -L./soso -labc
[root@localhost test]# LD_LIBRARY_PATH=./soso ./main
C is running
B is running
hello world
B is end
C is end
5、分别制作三个so,分别链接,发现不行,原因未知
[root@localhost test]# g++ -fPIC -shared -o ./soso/liba.so a.cpp
[root@localhost test]# g++ -fPIC -shared -o ./soso/libb.so b.cpp
[root@localhost test]# g++ -fPIC -shared -o ./soso/libc.so c.cpp
[root@localhost test]# g++ -o main -L./soso -la -lb -lc
/usr/bin/ld: /usr/lib/gcc/x86_64-linux/4.8.5/../../../../lib64/crt1.o: undefined reference to symbol '__libc_start_main@@GLIBC_2.2.5'
/usr/bin/ld: note: '__libc_start_main@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
[root@localhost test]# g++ -o main -L./soso -lc -lb -la
/usr/bin/ld: /usr/lib/gcc/x86_64-linux/4.8.5/../../../../lib64/crt1.o: undefined reference to symbol '__libc_start_main@@GLIBC_2.2.5'
/usr/bin/ld: note: '__libc_start_main@@GLIBC_2.2.5' is defined in DSO /usr/lib64/libc.so.6 so try adding it to the linker command line
/usr/lib64/libc.so.6: could not read symbols: Invalid operation
collect2: error: ld returned 1 exit status
制作makefile
将链接2个和3个动态文件及静态文件的方式做成makefile。
Makefile有三个非常有用的变量:
$@:目标文件
$^:所有的依赖文件
$<:第一个依赖文件。
.PHONY: buildabcso testabcso buildabso testabso buildabca testabca buildaba testaba clean
buildabcso: libabc.so
buildabso: libab.so
buildabca: libabc.a
buildaba: libab.a
libabc.so: a.o b.o c.o
g++ -shared -o $@ $^
libab.so: a.o b.o
g++ -shared -o $@ $^
libabc.a: a.o b.o c.o
ar crv $@ $^
libab.a: a.o b.o
ar crv $@ $^
a.o: a.cpp
g++ -c -fPIC $<
b.o: b.cpp
g++ -c -fPIC $<
c.o: c.cpp
g++ -c -fPIC $<
testabcso: libabc.so
g++ -o main -L. -labc
LD_LIBRARY_PATH=. ./main
testabso: libab.so
g++ c.cpp -o main -L. -lab
LD_LIBRARY_PATH=. ./main
testabca: libabc.a
g++ -o main -L. -labc
./main
testaba: libab.a
g++ c.cpp -o main -L. -lab
./main
clean:
rm -f *.o *.so *.a main
运行make buildabcso
构建生成libabc.so,运行make testabcso
使用该libabc.so动态链接库生成main可执行文件并执行,其他build和test同理。
makefile工作原理:
1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果make后有对应指令,会去.PHONY中找到对应指令;否则缺省的情况下,它会找第一条指令作为目标指令,在这里就是即buildabcso。make会把匹配到的指令作为最终的目标文件。
3、make发现buildabcso不存在,于是需要检查buildabcso该文件的依赖,在这里是libabc.so:
①、如果发现libabc.so已经存在,直接执行,由于buildabcso的行为啥都没定义,只有个依赖前缀libabc.so,所以执行buildabcso时其实啥都没做,即:
[root@localhost test]# make buildabcso
make: Nothing to be done for `buildabcso'.
②、如果发现libabc.so不存在,或是buildabcso所依赖的libabc.so文件修改时间要比buildabcso这个文件新,再检查libabc.so的依赖,也就是a.o b.o c.o,如果a.o和b.o和c.o三个文件已存在,才能执行g++ -shared -o $@ $^
生成libabc.so,如果三个.o不存在,继续查对应依赖生成,一直这样迭代下去。。。
4、make会一层一层去找文件的依赖关系,直到最终编译出第一个目标文件,如果某个命令执行失败,则make会停止。