C/C++文件变成可执行文件的四步
【预处理】 处理#开头的;
#include <>
将头文件在当前位置展开;#define a 1
会将a替换成实际的值1,#ifdef X #ifndef
判断X是否成立,成立就将其后语句加入当前位置【编译】 将预处理完的文件进行语法和语义分析及优化后生成 <汇编源代码文件.s> ,同.c源码是文本可查看
【汇编】 将 <汇编源代码文件.s> 进行汇编,生成二进制的ELF文件,但由于没有链接会找不到printf等函数而无法执行
【链接】 将汇编生成的二进制文件和其引用函数所在的库文件、二进制文件合成可以在特定平台运行的文件
1 | # 链接动态库时把 printf函数的位置信息 链接到 生成的二进制ELF文件 中去,等运行时根据 printf函数的位置信息 去系统中找相关运行库 |
GCC编译器的相关知识
- GCC的常用参数
1 | -E 预处理后只输出到屏幕不生成文件,可接-o指定生成文件,接-C不删除注释信息 |
动静态库文件仅在链接的过程存在区别,运行时仅动态库会被调用。
本质上说一个静态库可看成是一组目标文件(.o/.obj文件)的集合,静态库与汇编生成的目标文件(.a/.o/.obj)一起链接为可执行文件
静态库.a和目标文件.o文件格式相似,都是很多目标文件经过压缩打包后形成的一个文件
GCC在编译时都是从源码上面往下编译,GCC在链接时对命令行的参数处理顺序是从左到右,默认都是动态链接
GCC中库的链接顺序是从右往左进行,所以要把最基础实现的库放在最后,这样左边的lib就可以调用右边的lib中的代码
当一个函数的实现代码在多个lib都存在时,最左边的lib代码最后link,所以也将最终保存下来
GCC库和MSVC库的辨认
GCC 的动态库一般是
.so
后缀的,静态库.a
MSVC的动态库一般是.dll
后缀的,静态库.lib
- 库的搜索路径顺序
1 | 1) LD_LIBRARY_PATH |
- 如何查看库文件的相关信息
1 | ar -t libz.a // 查看静态库中包含的 <目标文件.o> |
MAKE编译中常用的一些变量
- gcc和make并不会主动从环境变量中读取CFLAGS/CXXFLAGS/LDFLAGS,但是可以传给Makefile。
1 | CFLAGS 用于 C 编译器的参数 |
链接库和头文件搜索路径的先后顺序相似:GCC中-I和-L指定路径的顺序,GCC默认环境变量指定的路径
也可以更改gcc和g++默认的环境变量:
1 | GCC使用:export C_INCLUDE_PATH=XXX:$C_INCLUDE_PATH |
实现静态链接库
参数-static和-shared可以改变GCC默认的链接方式,如果指定了
-static
这个选项在连接时对项目所有依赖库都尝试去搜索名为lib<name>.a
的静态库文件,如果找不到就报错。它不仅搜索我们用的库还包括编译器自带的库,还要包括所有被间接引用的第三方库编译器自带C/C++基础库的静态链接
1 | -static // 会将所以有用到的外部库全部静态链接 |
- 有选择的进行静态编译,连接程序ld提供了一个
-Bstatic
选项用于对跟在它后面的所有库执行静态连接
1 | # 通过-Wl将GCC的命令行参数传递给链接器ld,注意:-Wl后的 `,` 必不可少,如果传递多个参数之间用它分隔 |
- 链接的优先级:
1 | 首先是GCC的链接选项 -Wl,-Bstatic 和 -Wl,-Bdynamic 指定的链接动态库或者静态库 |
- 如果使用MinGW编译时有找不到Win32库的情况,可以查看需要包含的Windows库
例如:undefined reference to __imp_GetDeviceCaps
复制 GetDeviceCaps
到上面搜索可推断要加 -lgdi32
- 对于GCC,库的顺序也很重要。提供符号的库应在链接器命令行中引用该符号的对象之后提及。还需要确保您包含的每个库实际上是静态构建和使用的
使用CMAKE将GNU格式库转换为MSVC格式:在Win上使用MinGW库时,可以定义
CMAKE_GNUtoMS
变量来自动将GCC格式库.dll和.a
转换为微软编译器支持的.lib
格式。解决MinGW64运行缺少
libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll
的问题,静态链接它们-static-libgcc -lstdc++ -lgcc_eh -lpthread -static
Linux下可以通过类似
-l:libevent.a -l:libevent_pthreads.a
来静态链接指定库-levent -levent_pthreads