文章目录
参考:https://blog.csdn.net/Three_dog/article/details/103688043
事前准备
文件夹中创建main.cpp,hellow.cpp,hellow.h文件,文件中的代码如下:
//hellow.h
#ifndef _HELLOW_H_
#define _HELLOW_H_
class Hellow
{
public:
Hellow();
void hi();
~Hellow();
};
#endif //Hellow
//hellow.cpp
#include "hellow.h"
#include <iostream>
using namespace std;
Hellow::Hellow()
{
}
void Hellow::hi()
{
cout << "hello" << endl;
}
Hellow::~Hellow()
{
}
#include "hellow.h"
using namespace std;
int main()
{
Hellow h;
h.hi();
return 0;
}
一.g++ 编译代码的过程示例
g++编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 汇编程序 (assembler) → 目标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)。
-
可以使用
g++ -E
让g++ 在预处理之后停止编译过程,生成*.ii
(.c
文件生成的是*.i
) 文件。因为上面写的main.cpp
中没有任何预编译指令,所以预编译生成与源文件几乎没有差别。这里预编译一下hellow.cpp
与main.cpp
文件g++ -E hellow.cpp hellow.h -o hellow.ii g++ -E main.cpp -o main.ii
编译完成后,#include引入的内容 被全部复制进预编译文件中,除此之外,如果文件中有使用宏定义也会被替换处理。预编译过程最主要的工作,就是宏命令的替换,#include命令的工作就是单纯的导入,这里其实并不限制导入的类型,甚至可以导入.cpp、.txt等等。 -
g++ 编译阶段:C++ 语法错误的检查,就是在这个阶段进行。在检查无误后,g++ 把代码翻译成汇编语言。可以使用-S 选项进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
g++ -S main.ii -o main.s g++ -S hellow.ii -o hellow.s
-
g++ 汇编阶段:生成目标代码 *.o
g++ main.cpp -c -o main.o g++ hellow.cpp -c -o hellow.o
-
链接生成可执行文件
g++ main.o hellow.o -o main
-
run
./main
二.使用g++生成静态链接库和动态链接库
生成静态链接库
使用ar
命令进行“归档”(.a的实质是将文件进行打包)
ar crsv libhellow.a hellow.o
r
替换归档文件中已有的文件或加入新文件 (必要)c
不在必须创建库的时候给出警告s
创建归档索引v
输出详细信息
生成动态链接库
使用g++ -shared
命令指定编译生成的是一个动态库
g++ hellow.cpp -fPIC -shared -o libhellow.so.0.1
- -shared:告诉编译器生成一个动态链接库
- -o:指示实际生成的动态链接库(这里是libtest.so.0.1)
- -fPIC
fPIC的全称是 Position Independent Code, 用于生成位置无关代码(看不懂没关系,总之加上这个参数,别的代码在引用这个库的时候才更方便,反之,稍不注意就会有各种乱七八糟的报错)。
使用-fPIC选项生成的动态库,是位置无关的。这样的代码本身就能被放到线性地址空间的任意位置,无需修改就能正确执行。通常的方法是获取指令指针的值,加上一个偏移得到全局变量/函数的地址。
关于PIC参数的详细解读:点此链接
在gcc中,如果指定-shared不指定-fPIC会报错,无法生成非PIC的动态库,不过clang可以。
库中函数和变量的地址是相对地址,不是绝对地址,真实地址在调用动态库的程序加载时形成。
动态库的名称有别名(soname),真名(realname)和链接名(linker name)。
-
真名是动态库的真实名称,一般总是在别名的基础上加上一个小的版本号,发布版本构成
-
别名由一个前缀lib,然后是库的名字加上.so构成,例如:libQt5Core.5.7.1
-
链接名,即程序链接时使用的库的名字,例如:-lQt5Core
在动态链接库安装的时候总是复制库文件到某个目录,然后用软连接生成别名,在库文件进行更新的时候仅仅更新软连接即可。 -
生成的库文件总是以libXXX开头,这是一个约定,因为在编译器通过-l参数寻找库时,比如-lpthread会自动去寻找libpthread.so和libpthread.a。
如果生成的库并没有以lib开头,编译的时候仍然可以连接到,不过只能以显示加在编译命令参数里的方式链接。例如:g++ main.o hellow.so
三.静态编译与动态编译程序
静态编译程序
g++ main.o libhellow.a -o main
编译完成后可以运行a.out
查看效果,通过ldd
命令查看运行a.out
所需依赖,可以看到静态编译的程序并不依赖libtest库。
动态编译程序
g++ main.o ./libhellow.so.0.1 -o main
四.动态链接库的添加方式
-
编译程序时直接指定动态链接库所在目录
g++ main.o ./libhellow.so.0.1 -o main
或者
g++ main.o /home/nflg/a/libhellow.so.0.1 -o main
-
通过-L指定动态链接库所在文件夹
g++ main.o -lhellow -L. -o main
-
通过LIBRARY_PATH添加动态链接库
LIBRARY_PATH=/home/nflg/a/ g++ main.o -lhellow -o main
-
直接将动态链接库添加到、/usr/lib文件夹中
五.动态链接库以及头文件搜索顺序
动态链接库
运行时库的查找顺序:
- 编译目标代码时指定的动态库搜索路径(-rpath);
- 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
- 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
- 默认的动态库搜索路径/lib;
- 默认的动态库搜索路径/usr/lib.
头文件
- 先搜索当前目录
- 然后搜索-I指定的目录
- 再搜索gcc的环境变量CPLUS_INCLUDE_PATH(C程序使用的是C_INCLUDE_PATH)
- 最后搜索gcc的内定目录
/usr/include
/usr/local/include
/usr/lib/gcc/x86_64-redhat-linux/4.1.1/include