1`构建阶段、构建目标和使用
构建
构建阶段
构建:将源码(高级语言)转换为可执行程序(二进制)
上述是c语言
c++: .cpp, .h -> .ii -> .s -> .obj -> .exe
分别由 预处理器、编译器 / 解释器、汇编器、链接器 执行
构建流程:
- 预处理(Preprocessing)根据预处理指令 对源代码进行 插入 / 替换 / 丢弃……
- 插入文件
- #include “” 首先从当前目录查找
- #include <> 首先从系统目录查找
- 替换为实际内容
- 自定义宏
- #define
- 预定义宏
- __ FUNTION __ 获取当前函数名
- __ LINE __ 获取当前代码行号
- __ FILE __ 获取当前文件名
- __ DATE __ 获取当前日期
- __ TIME __ 获取当前时间
- __ STDC_VERSION __ 获取当前编译器的版本
- ……
- 自定义宏
- 根据条件决定哪部分需要编译,可以使目标程序变小,运行时间变短
- 条件判断
- #ifdef //某个宏是否已经定义(if define)
- #ifndef //某个宏是否没有定义(if not define)
- #else
- #endif
- #if //常量表达式是否为真
- #elif
- #else
- 条件判断
- 插入文件
- 编译(Compilation)仅对源文件编译(但头文件会include到源文件中),会进行语法分析,翻译成汇编语言代码,会产生符号表(标识符), 和为符号表分配相对地址(相对每个文件)
- 汇编(Assembly)汇编代码翻译成机器码(二进制),会根据各个段分配更精确的相对地址
- 链接(Linking)将多个二进制文件链接在一起,生成可执行文件,合并给符号分配绝对地址并替换,从而使得代码可以通过符号地址执行相应操作
Parser解析器
解析器 / 语法分析器 是编译器的核心组件之一,它会对输入的字符序列 利用扫描器词法分析 输出切割后的单词流,对单词流 根据语法分析器 构建出相应的 语法树 / 精简过的抽象语法树
抽象语法树(AST)是一种 树形数据结构,它以一种更抽象、更易于处理的方式表示输入数据的语法结构
调试符号
- 原理:在构建阶段时保留一定的源码内容(标识符,行号,数据类型……),并将它们和最终的二进制建立映射的关系,从而运行二进制时可追溯源文件中对应的信息,以便在源文件进行调试
- 输出文件:vs(.pdb)
- 可读性:直接打开文件显示乱码,应在ide的可视化调式界面查看,比如(断点,变量监视器,局部变量值查看器,堆栈查看器……窗口)
- 作用:可断点逐句执行,查看变量值,查看堆栈(函数调用)……
构建目标
静态库:
- 静态库 windows->(.lib), linux->(.a)(.la),包含实际数据和符号名
- 不可单独执行
- 在其他程序中构建时,从库复制数据,和程序其他代码组合为可执行文件(.exe)文件
- 静态链接:链接时机:生成可执行程序前
- 发布时,不需要静态库,仅可执行文件就可以运行
- 优点:执行速度略快
- 缺点:
- exe文件比较大;
- 静态库中不能再包含其他库;
- 耦合度高,当库内容更改时,需要重新编译整个程序
动态库:
- 动态库:(.lib文件(引入库/导入库)和.dll文件).lib包含分配地址后的符号表, dll包含实际的函数和数据,linux中以(.so)为后缀
- 不可单独执行
- 在其他程序中构建时,只需要链接引入库文件,不用从库复制数据,
- 动态链接:链接时机:运行可执行程序时
- 发布时,需要发布dll库,运行时从dll加载所需的数据
- 优点:
- 仅需要装载需要使用的数据到内存,动态的加载卸载,减少exe文件的大小;
- 动态库中可以包含其他库;
- 耦合度低,库内容更改时,只需要替换dll,不需要重新编译整个程序
- 缺点:执行速度略慢
使用库
下面以vs为例
构建目标:
- vs -> project -> properties -> confuguration properties -> general -> confuguration type 选择构建类型
构建输出目录:
- -> general -> output directory 设置输出目录
构建中间件输出目录:
- -> general -> intermediate directory 设置输出目录
静态库:
- 头文件
- #include 直接通过预处理指令包含,可以为绝对路径/拷贝到和项目同一目录后的相对目录
- -> VC++directories -> include directories
- -> c/c++(要创建首个源文件才可以看到) -> general -> Additional include directories 添加头文件目录
- lib
- 项目右击 -> add -> existing item 添加库目录,拷贝到和项目同一目录
- -> VC++directories -> library directories
- -> linker -> input -> Additional dependencies 添加库名字,-> linker -> general -> Additional library directories 添加库目录
动态库:
- 头文件
- 和上面一样
- lib
- 和上面一样
- dll
- 拷贝到项目的exe目录下
- -> VC++directories -> executable directories
DLL库导入导出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//myLibrary.h
#ifdef DLLEXPORT
#define _DLL_DECLARE_ declspec(dllexport)
#else
#define _DLL_DECLARE_ declspec(dllimport)
_DLL_DECLARE_ double myFunc(double a, double b);//导出
_DLL_DECLARE_ double myFunc(double a, double b);
class _DLL_DECLARE_ MyClass {
public:
MyClass();
void doSomething();
};
//myLibrary.cpp
#define DLLEXPORT // 定义宏
#include "myLibrary.h" //在宏的下面包含
//……函数实现
展开
__declspec关键字可以用来修饰函数/变量/类
宏的定义要放在cpp中,因为头文件是要include到main的,因此宏应该对main不可见
1
2
3
4
5
6
7
8
9
10
11
12
13
//main.cpp
#include "myLibrary.h"//导入
#include <iostream>
int main() {
// 使用导入的函数
double result = myFunc(3.14, 2.71);
// 使用导入的类
MyClass obj;
obj.doSomething();
return 0;
}
展开
本文由作者按照 CC BY 4.0 进行授权
