程序编译顺序
程序编译需要通过调用
- 预处理器
main.c->main.i
指令:gcc -E main.c -o main.i
作用:#define
删除、展开宏、处理带#
的条件编译指令、条件语句中符合条件的会保留,不符合的会删除,插入include
、删除注释、添加行号文件指示
- 编译器
main.i->main.s
指令:gcc -S main.i -o main.s
**作用:**把高级语言编译成机器可识别的汇编语言
- 汇编器
main.s->main.o
指令:gcc -c main.s -o main.o
**作用:**将汇编文件生成二进制文件(可重定位目标文件)
- 链接器
- 将
main.o
与其依赖的可重定位目标文件链接起来生成可执行目标文件
**作用:**链接器把目标文件与所需要的附加的目标文件(如静态链接库、动态链接库)
静态库
编译器提供一种机制,将所有相关的目标模块打包成为一个单独的文件称为静态库。
当链接器构造一个输出的可执行文件时,它只复制静态库里被应用程序引用的目标模块
静态库被提出之前用过的方法
- 让编译器辨认出对标准函数的调用,并直接生成相应的代码
- 缺点:
增加了编译器的复杂性
每次修改一个标准函数时就需要一个新的编译器版本
- 将所有标准
c
函数都放在一个单独的可重定位目标模块
- 缺点:
每个正在运行的程序都将这些函数的副本放在内存中,极大的浪费了内存。
- 为每个标准函数创建一个独立的可重定位文件,把他们放在一个大家都知道的目录中。
- 缺点:
要求程序员显式的链接合适的目标模块到他们的可执行文件中,这是一个容易出错且耗时的过程。
构建静态库
目录结构
. ├── stlib │ ├── StaticMath.cpp │ └── StaticMath.h └── test └── staticmathtest.cpp
在stlib
下构建
> g++ -c StaticMath.cpp # 生成可重定位目标文件StaticMath.o > ar -crv libstaticmath.a StaticMath.o # 打包
在test
下链接
# 使用 > g++ -c staticmathtest.cpp -I../stlib/ > g++ -static -o proc staticmathtest.o -L../stlib/ -lstaticmath
-L
去哪个目录下找-l
下的什么文件
- 这里是去父目录的
stlib
中找libstaticmath.a
文件
- 参数说明:要做一般都放在相同目录
-L:
表示要连接的库所在目录-I./stlib/
表示指定头文件的目录为父目录下的stlib
目录-l(小写L):
指定链接时需要的库,去掉前缀和后缀
最后执行即可
> ./proc
注意点
如果依赖的静态库之间不是独立的则需要对他们的调用顺序排序
例如:
foo.c
调用libx.a
和libz.a
的函数,而这两个库有调用liby.a
中的函数,那么libx.a
和libz.a
必须出现在liby.a
之前
> gcc foo.c libx.a libz.a libx.a
静态库的缺点
静态库将显式的将他们的程序与更新了的库重新连接,并且这些代码都会被复制到每个运行进程的文本段。
动态库
所有引用该库的可执行目标文件共享这个.so
文件中的代码和数据,而不是像静态库的内容那样被复制和嵌入到引用他们的可执行文件中
动态库构建
目录结构
. ├── dylib │ ├── DynamicMath.cpp │ └── DynamicMath.h └── test └── dynamcimathtest.cpp
在dylib
目录下构建
> g++ -c -fPIC -c DynamicMath.cpp > g++ -shared -o libdynamicmath.so DynamicMath.o
现在就有了一个libdynamicmath.so
动态库
在test
目录下链接
- 汇编得到可重定位目标文件
> g++ -c dynamcimathtest.cpp -I../dylib
- 链接器链接
> g++ -o proc dynamcimathtest.o -L../dylib -ldynamicmath
- 然后将这个库放在
/usr/lib/
或者/lib/
下则ld
就默认能够找到
sudo cp ../dylib/libdynamicmath.so /lib/
- 如果放在其他目录则可以添加到
/etc/ld.so.cache
文件然后ldconfig
即可
./proc
上述代码结构我放在github
上可以下载下来试一试