10.C++从源程序到可执行程序的过程

2026-01-23
1623 字 · 8 分钟

🔬 C++从源程序到可执行程序的过程

📖 内容概览

  • 了解C++程序从源代码到可执行文件的完整编译流程
  • 掌握预处理、编译、汇编、链接四个阶段的核心任务
  • 理解各阶段生成的中间文件格式和作用
  • 学习静态链接和动态链接的区别与应用场景

🎯 编译流程概述

C++程序的编译过程是一个多阶段的转换过程,从源代码文件最终生成可执行程序:

📝 源程序 (.cpp, .h)
🔍 预处理阶段 → 预处理文件 (.i)
💻 编译阶段 → 汇编文件 (.s)
🔧 汇编阶段 → 目标文件 (.o, .obj)
🔗 链接阶段 → 可执行文件 (.exe, ELF, Mach-O)

🔄 详细编译阶段

📋 预处理阶段 (Preprocessing)

输入:源代码文件 (.cpp) 和头文件 (.h) 输出:预处理后的C++文件 (.i) 工具:预处理器 (cpp) 预处理阶段主要完成以下工作:

预处理操作详细说明
宏展开替换所有#define定义的宏,包括带参数的宏
条件编译处理#if#ifdef#ifndef#elif#else#endif等条件指令
文件包含#include指令指向的头文件内容直接插入到当前文件
注释删除删除所有///* */形式的注释
行号标记添加行号和文件名信息,便于编译器生成调试信息
特殊指令处理处理#pragma等编译器特定指令
命令示例
Terminal window
g++ -E test.cpp -o test.i # 只进行预处理,生成test.i文件

💻 编译阶段 (Compilation)

输入:预处理文件 (.i) 输出:汇编语言文件 (.s) 工具:编译器 (g++) 编译阶段是最复杂的阶段,包含多个子阶段:

2.1 词法分析 (Lexical Analysis)

  • 将源代码字符串分解为词法单元(Tokens)
  • 识别关键字、标识符、常量、运算符、标点符号等
  • 生成词法单元流供语法分析使用
  • 工具:lex(自动生成词法分析器)

2.2 语法分析 (Syntax Analysis)

  • 将词法单元流组织成抽象语法树 (AST)
  • 检查源代码是否符合C++语法规则
  • 识别语法结构如函数、类、表达式、语句等
  • 工具:yacc/bison(自动生成语法分析器)

2.3 语义分析 (Semantic Analysis)

  • 检查代码的语义正确性
  • 进行类型检查和类型转换
  • 处理变量声明和作用域
  • 检查函数调用的参数类型匹配
  • 识别符号并构建符号表

2.4 中间代码生成

  • 将AST转换为中间表示 (IR),如三地址码
  • 进行初步优化(如常量折叠、死代码消除)

2.5 代码优化

  • 对中间代码进行各种优化,提高程序性能
  • 包括局部优化、循环优化、全局优化等

2.6 目标代码生成

  • 将优化后的中间代码转换为汇编语言
  • 针对特定CPU架构生成最优汇编代码
Terminal window
g++ -S test.i -o test.s # 从预处理文件生成汇编文件

🔧 汇编阶段 (Assembly)

输入:汇编语言文件 (.s) 输出:目标文件 (.o 或 .obj) 工具:汇编器 (as) 汇编阶段的主要任务:

  • 汇编语言指令转换为机器语言指令
  • 每条汇编语句对应一条机器指令
  • 生成二进制格式的目标文件
  • 目标文件包含:
    • 机器码
    • 符号表
    • 重定位信息
    • 调试信息
Terminal window
g++ -c test.s -o test.o # 从汇编文件生成目标文件

🔗 链接阶段 (Linking)

输入:目标文件 (.o) 和库文件 (.a, .so, .dll) 输出:可执行文件 (.exe, ELF, Mach-O) 工具:链接器 (ld) 链接阶段的主要任务:

4.1 符号解析

  • 解析目标文件中的外部符号引用
  • 将符号名与对应的内存地址关联

4.2 重定位

  • 调整目标文件中符号的内存地址
  • 将所有目标文件合并到一个统一的地址空间

4.3 静态链接 vs 动态链接

链接类型特点优点缺点
静态链接链接时将库代码直接复制到可执行文件运行时无需依赖外部库,移植性好可执行文件体积大,库更新需要重新编译
动态链接运行时才加载库文件,共享库代码可执行文件体积小,库更新无需重新编译运行时依赖外部库,可能出现版本兼容问题
Terminal window
g++ test.o -o test.exe # 将目标文件链接为可执行文件
g++ test.o -L. -lmylib -o test.exe # 链接自定义库

📋 中间文件格式

文件扩展名文件类型生成阶段主要内容
.cpp, .ccC++源代码文件源代码编写程序员编写的C++代码
.h, .hpp头文件源代码编写声明、宏定义、模板等
.i预处理后的C++文件预处理阶段展开了宏和包含了头文件的C++代码
.s汇编语言文件编译阶段针对特定CPU的汇编指令
.o, .obj目标文件汇编阶段二进制机器码、符号表、重定位信息
.a, .lib静态库文件库编译多个目标文件的归档
.so, .dll动态库文件库编译可在运行时加载的共享代码
.exe, ELF, Mach-O可执行文件链接阶段可直接运行的二进制文件

💡 实际编译命令示例

单文件编译

Terminal window
g++ hello.cpp -o hello # 一步完成所有编译链接过程
g++ -Wall -g hello.cpp -o hello # 开启警告和调试信息

多文件编译

Terminal window
# 方式1:一步编译
g++ main.cpp func1.cpp func2.cpp -o program
# 方式2:分步编译(适合大型项目)
g++ -c main.cpp -o main.o # 编译main.cpp
g++ -c func1.cpp -o func1.o # 编译func1.cpp
g++ -c func2.cpp -o func2.o # 编译func2.cpp
g++ main.o func1.o func2.o -o program # 链接所有目标文件

⚖️ 静态链接与动态链接对比

对比维度静态链接动态链接
文件体积大(包含库代码)小(仅包含库引用)
启动速度快(无需加载外部库)慢(需要加载外部库)
内存占用高(多个程序重复加载相同库)低(多个程序共享同一库)
更新维护困难(库更新需重新编译)容易(库更新无需重新编译)
依赖关系无外部依赖依赖外部库文件
安全性高(不易被篡改)较低(库文件可能被替换)

Thanks for reading!

10.C++从源程序到可执行程序的过程

2026-01-23
1623 字 · 8 分钟

已复制链接

评论区

目录