C++ 编译过程详解

4个阶段

C++程序的编译过程是一个多阶段的复杂过程,将人类可读的源代码转换为机器可执行的目标代码。主要分为以下四个阶段:

  1. 预处理 (Preprocessing)
    功能:
    • 处理所有以#开头的预处理指令
    • 展开宏定义
    • 包含头文件内容
  • 条件编译

    主要操作:

    • #include - 将指定文件内容插入到该指令位置
    • #define - 定义宏
  • #ifdef/#ifndef/#endif - 条件编译

    • #pragma - 编译器特定指令

    输入文件:

    • .cpp, .hpp, .h等源代码文件

    输出文件:

    • .i (预处理后的文本文件)
  1. 编译 (Compilation) --- 编译器

    功能:

    • 将预处理后的代码转换为汇编代码
    • 进行语法和语义分析
    • 生成中间表示(IR)
    • 优化代码

    主要操作:

    • 词法分析 - 将源代码分解为标记(token)
    • 语法分析 - 构建抽象语法树(AST)
    • 语义分析 - 检查类型、作用域等
    • 代码生成 - 产生目标平台的汇编代码

    输入文件:

    • .i文件

    输出文件:

    • .s (汇编代码文件)
  2. 汇编 (Assembly) --- 汇编器

    功能:

    • 将汇编代码转换为机器码
    • 生成可重定位的目标文件

    主要操作:

    • 汇编器读取由编译器生成的汇编代码文件
    • 汇编器逐行读取汇编代码,并将每条汇编指令转换成对应的机器指令
    • 汇编器记录所有定义的符号及其地址,并生成符号表
    • 汇编器记录需要在链接时进行重定位的部分,并生成重定位信息
    • 汇编器将所有信息打包成一个目标文件,通常是一个二进制文件,如 .o 文件

    输入文件:

    • .s文件

    输出文件:

    • .o或.obj (目标文件)
  3. 链接 (Linking) --- 链接器

    功能:

    • 将多个目标文件和库文件合并
    • 解析符号引用
    • 生成最终可执行文件

    主要操作:

    • 收集目标文件 - 链接器收集所有需要链接的目标文件(.o 文件)和库文件(如 .a 或 .so 文件)。
    • 符号解析 - 确保所有符号都有定义。链接器解析各个目标文件中的符号引用,并查找相应的符号定义。
      如果一个目标文件中引用了一个符号,链接器会在其他目标文件或库文件中查找该符号的定义。
      如果找不到某个符号的定义,链接器会产生一个错误,指出未定义的符号。
    • 重定位 - 调整代码和数据地址。链接器根据最终的布局对代码和数据进行重定位。
      这涉及到更新目标文件中的重定位信息,确保符号在最终的可执行文件或库文件中具有正确的地址。
    • 合并符号表 - 链接器合并所有目标文件中的符号表,生成一个全局的符号表。
      确保每个符号在整个程序中唯一,并且可以被正确引用。
    • 插入库文件 - 如果程序依赖于外部库,链接器会插入这些库文件中的相关部分。
      动态库(如 .so 文件)通常在运行时加载,而静态库(如 .a 文件)则在链接时直接插入到最终的可执行文件中。
    • 生成最终文件 - 链接器将所有处理后的代码和数据组合成一个可执行文件或库文件。
      可执行文件(如 .exe 文件在 Windows 中,.out 或直接为程序名称在 Linux 中)包含了所有必要的信息,使得程序能够在目标平台上运行。

    输入文件:

    • 多个.o文件和库文件

    输出文件:

    • .exe(Windows)或可执行文件(Unix-like)

完整编译流程示例

shell 复制代码
main.cpp + other.cpp + headers
       ↓ 预处理
main.i + other.i
       ↓ 编译
main.s + other.s
       ↓ 汇编
main.o + other.o
       ↓ 链接
    executable

常用编译器命令示例

GCC/Clang

shell 复制代码
# 完整编译过程
g++ main.cpp other.cpp -o program

# 分步编译
g++ -E main.cpp -o main.i    # 预处理
g++ -S main.i -o main.s      # 编译
g++ -c main.s -o main.o      # 汇编
g++ main.o other.o -o program # 链接

MSVC (Visual Studio)

shell 复制代码
cl /EHsc /c main.cpp        # 编译
cl /EHsc /c other.cpp       # 编译
link main.obj other.obj /OUT:program.exe  # 链接

理解C++编译过程对于调试复杂问题、优化程序性能和构建大型项目至关重要

GCC 常用编译选项

常用选项 描述
-E 预处理,开发过程中想快速确定某个宏可以使用"-E -dM"
-S 编译
-c 把预处理、编译、汇编都做了,但是不链接
-o 指定输出文件
-I 指定头文件目录
-L 指定链接时库文件目录
-l 指定链接哪一个库文件

6个阶段

如果按照更细的粒度划分,C++ 编译过程可以分为以下 6个阶段:

  1. 预处理(Preprocessing)

    处理 #include、#define、#ifdef 等宏指令

    展开头文件,生成 纯C++代码(.i 文件)

  2. 词法分析(Lexical Analysis / Tokenization)

    将源代码拆解成 Token(标记),如关键字、标识符、运算符等

    例如:int a = 10; → int a = 10 ;

  3. 语法分析(Syntax Analysis / Parsing)

    将 Token 组织成 抽象语法树(AST, Abstract Syntax Tree)

    检查语法是否正确(如括号匹配、语句结构)

  4. 语义分析(Semantic Analysis)

    检查变量类型、作用域、函数重载等是否符合语义规则

    例如:int a = "hello"; 会报错(类型不匹配)

  5. 代码生成与优化(Code Generation & Optimization)

    将 AST 转换为 目标机器码(汇编代码 .s 文件)

    进行优化(如常量折叠、死代码删除、循环优化等)

  6. 汇编与链接(Assembly & Linking)

    汇编:将汇编代码(.s)转换为机器码(.o 或 .obj)

    链接:合并多个 .o 文件,解析外部符号,生成可执行文件(.exe 或 .out)

为什么会有不同划分方式?

  1. 编译器实现不同

    某些编译器(如 GCC、Clang、MSVC)可能内部阶段划分略有不同。

    例如,GCC 的 -fdump-tree-all 可以输出中间表示(IR),能看到更多细节。

  2. 教材/课程侧重点不同

    编译原理课程可能更关注 前端(词法、语法、语义分析),而工程上更关注 预处理、编译、链接。

  3. 优化阶段可能单独列出

    现代编译器(如 LLVM)会在多个阶段进行优化,因此优化可能被视为独立步骤。

总结

4阶段(传统) 6阶段(细化) 主要任务
预处理 预处理 宏扩展、头文件包含
编译 词法分析 源代码-->Token
语法分析 Token-->AST
语义分析 类型检查、作用域分析
代码生成与优化 AST-->汇编代码+优化
汇编 汇编 汇编代码-->机器码(.o)
链接 链接 合并.o,解析符合,生成可执行文件

本质上,6阶段划分只是把"编译"阶段拆解得更细,核心流程仍然是:

源代码 → 预处理 → 编译(前端+后端)→ 汇编 → 链接 → 可执行文件

如果你在某个资料里看到6阶段划分,那很可能是为了更详细地解释编译器的内部工作原理。

相关推荐
Littlehero_1211 小时前
QT自定义控件之热换站远程监控系统
c++·qt
努力努力再努力wz1 小时前
【Qt入门系列】一文掌握 Qt 常用显示类控件:QLCDNumber、QProgressBar 与 QCalendarWidget
c语言·开发语言·数据结构·数据库·c++·git·qt
C++ 老炮儿的技术栈2 小时前
如何利用 OpenCV 将图像显示在对话框窗口上
c语言·c++·人工智能·qt·opencv·计算机视觉·github
凯瑟琳.奥古斯特3 小时前
力扣1003题C++解法详解
开发语言·c++·算法·leetcode·职场和发展
hunterkkk(c++)3 小时前
SPFA最短路径算法(c++)
java·c++·算法
c238563 小时前
C++11final与override6、智能指针
开发语言·c++
kupeThinkPoem4 小时前
c++是否会读到部分写入的数据?
c++
luj_17684 小时前
马克思的跨学科学术体系
c语言·开发语言·c++·经验分享·算法
阿文的代码库4 小时前
干货分享|C++运算符重载知识点
java·c++·算法