文章

1`构建阶段、构建目标和使用

构建

构建阶段

构建:将源码(高级语言)转换为可执行程序(二进制)

1742209654531

上述是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 进行授权
本页总访问量