
编译、链接、头文件
编译过程
预编译
预编译是使用预编译器cpp处理.c源文件和.h头文件,最终生成一个.i的文件。预编译过程就是处理源代码中以#开头的预编译指令,如#include #define 等。预编译过程等价于如下命令:
gcc -E hello.c -o hello.i
# or
cpp hello.c > hello.i
编译
编译的过程就是将预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化,最后生成 .s 汇编代码文件。编译过程是整个程序构建的核心部分,也是最复杂的部分之一。编译过程等价如下命令:
gcc -S hello.i -o hello.s
汇编
汇编器是将汇编代码转变成机器可以执行的指令。最后生成一个 .o 目标文件。汇编过程等价如下命令:
gcc -c hello.s -o hello.o
# or
as hello.s -o hello.o
链接
链接的作用就是将我们编译出来的目标文件和我们代码所用到的库文件一起打包成一个可执行文件的过程。例如hello.c中的打印函数printf,这个函数不是凭空出现的,在链接的过程中就要连同对应库文件一起打包,最终可执行文件才能正常运行。
静态库与动态库
静态库和动态库的载入时间是不一样的。
静态库的代码在编译的过程中已经载入到可执行文件中,所以最后生成的可执行文件相对较大。
动态库的代码在可执行程序运行时才载入内存,在编译过程中仅简单的引用,所以最后生成的可执行文件相对较小。
静态库和动态库的最大区别是,静态库链接的时候把库直接加载到程序中,而动态库链接的时候,它只是保留接口,将动态库与程序代码独立,这样就可以提高代码的可复用度和降低程序的耦合度。
静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
无论静态库,还是动态库,都是由.o文件创建的。
静态库
静态库的名字一般是libxxx.a
,在编译的时候直接编译进可执行文件中,运行环境中可以不用存在库文件,但是如果库文件更新了,可执行文件需要重新编译。
Linux下使用ar命令操作静态库。
参数 | 意义 |
---|---|
r | 将objfile文件插入静态库尾或者替换静态库中同名文件 |
x | 从静态库文件中抽取文件objfile |
t | 打印静态库的成员文件列表 |
d | 从静态库中删除文件objfile |
s | 重置静态库文件索引 |
v | 创建文件冗余信息 |
c | 创建静态库文件 |
一个简单的例子如下:
gcc -c hello.c -o hello.o
ar crv libhello.a hello.o
动态库
动态库中的代码是可执行文件在运行时加载执行的,也就是说,程序运行环境中要有动态库文件。一般动态库文件命名为libxxx.so
。动态库的优点就是方便升级,动态库变化了,可执行文件不用重新编译。
使用下面的命令就可以生成一个动态库文件libhello.so:
# -fPIC 是创建与地址无关的编译程序(pic,position independent code)
# -shared指定生成动态链接库
gcc -fPIC -shared -o libhello.so hello.c
include
#include <file>
这种写法用于include系统头文件,编译器首先搜索你通过-I
参数指定的路径,然后在标准系统路径中寻找,你也可以使用-nostdinc禁止搜索标准系统路径。
#include "file"
这种写法用于include你自己的头文件,编译器首先在当前目录(源文件所在目录)中搜索,此后的过程同#include <file>
。你也可以通过指定-I-
选项禁止搜索当前目录。
原文文档见 https://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
LD_LIBRARY_PATH VS LIBRARY_PATH
不论是动态库还是静态库,都需要-l
链接具体的库,而链接时所使用的搜索目录可以由LIBRARY_PATH
追加,如果是动态库,运行二进制文件时还需要再搜索一次相应的动态库,此时的搜索目录可以由LD_LIBRARY_PATH
追加。
如果想要查看一个二进制程序运行所需要的动态库以及是否找到,
可以使用 ldd xxx
命令。
如果有些动态库没找到,大概率是因为链接路径没有设置到位,可以考虑使用 export LD_LIBRARY_PATH=/path/to/library
手动加上链接路径。
ld默认读取的文件为/etc/ld.so.conf
,这个文件又会读取/etc/ld.so.conf.d
目录下的所有文件,所以只要在这两个地方的文件内容处添加上想要链接的路径就可以做到配置化。
不论你使用哪种方式设置好以后,建议都使用 ldconfig
命令确认一下配置。
最后
使用这条cpp -v /dev/null -o /dev/null
命令可以在输出的最后看到include搜索路径、库搜索路径。