C++ 代码到程序的生成过程 笔记

4 阅读3分钟

C++代码到可执行程序的生成过程分为四个主要阶段:

一、预处理阶段 (Preprocessing)

主要任务:

  • 处理以#开头的预处理指令
  • 生成纯净的C++源代码

具体操作:

// 源文件 example.cpp
#include <iostream>
#define PI 3.14159
#define SQUARE(x) ((x)*(x))

int main() {
    std::cout << "Area: " << PI * SQUARE(5) << std::endl;
    return 0;
}

预处理后会:

  1. 展开头文件:将#include <iostream>替换为iostream的内容
  2. 宏替换:将PI替换为3.14159SQUARE(5)替换为((5)*(5))
  3. 条件编译:处理#ifdef#ifndef#endif
  4. 删除注释

命令:

g++ -E example.cpp -o example.i  # 生成预处理文件

二、编译阶段 (Compilation)

主要任务:

  • 将预处理后的C++代码翻译成汇编代码

具体过程:

  1. 词法分析:将源代码分割成token(标识符、关键字、运算符等)
  2. 语法分析:构建抽象语法树(AST)
  3. 语义分析:检查类型、作用域等语义规则
  4. 中间代码生成:生成IR(Intermediate Representation)
  5. 代码优化:进行各种优化
  6. 目标代码生成:生成汇编代码

输出文件:

  • .s.asm 扩展名的汇编文件

命令:

g++ -S example.i -o example.s  # 生成汇编文件

三、汇编阶段 (Assembly)

主要任务:

  • 将汇编代码翻译成机器码(目标文件)

具体操作:

  • 将汇编指令逐条转换为对应的机器指令
  • 生成可重定位目标文件(Relocatable Object File)

目标文件包含:

  1. 代码段(.text):机器指令
  2. 数据段(.data):已初始化的全局/静态变量
  3. BSS段(.bss):未初始化的全局/静态变量
  4. 符号表:函数名、变量名等符号信息
  5. 重定位信息:地址需要后续调整

命令:

g++ -c example.s -o example.o  # 生成目标文件

四、链接阶段 (Linking)

主要任务:

  • 合并多个目标文件,解析符号引用,生成可执行文件

两种链接方式:

  1. 静态链接:将库代码复制到可执行文件中

    g++ example.o -static -o program
    
  2. 动态链接:运行时才加载共享库

    g++ example.o -o program
    

链接过程:

  1. 符号解析:找到每个符号的定义位置
  2. 重定位:调整代码和数据的地址
  3. 合并段:将各个目标文件的相同段合并
  4. 解析库依赖:链接标准库和第三方库

五、完整的编译流程示例

单文件编译:

# 完整流程(一步到位)
g++ example.cpp -o program

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

多文件编译:

// main.cpp
#include "math_utils.h"
int main() {
    return add(2, 3);
}

// math_utils.cpp
#include "math_utils.h"
int add(int a, int b) {
    return a + b;
}

// math_utils.h
int add(int a, int b);
# 分别编译再链接
g++ -c main.cpp -o main.o
g++ -c math_utils.cpp -o math_utils.o
g++ main.o math_utils.o -o program

六、常见编译选项

选项说明
-std=c++11/14/17/20指定C++标准版本
-O0/-O1/-O2/-O3优化级别
-g包含调试信息
-Wall开启所有警告
-I<dir>添加头文件搜索路径
-L<dir>添加库文件搜索路径
-l<library>链接指定库
-shared生成共享库
-fPIC生成位置无关代码

七、生成不同类型的目标

可执行文件:

g++ main.cpp -o program

静态库:

# 1. 编译为目标文件
g++ -c lib.cpp -o lib.o

# 2. 打包为静态库
ar rcs libmylib.a lib.o

# 3. 使用静态库
g++ main.cpp libmylib.a -o program
# 或
g++ main.cpp -L. -lmylib -o program

动态库:

# 1. 编译为动态库
g++ -shared -fPIC lib.cpp -o libmylib.so

# 2. 使用动态库
g++ main.cpp -L. -lmylib -o program

# 3. 设置运行时库路径
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

八、现代编译工具链

CMake示例:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyProject)

set(CMAKE_CXX_STANDARD 17)

add_executable(program main.cpp math_utils.cpp)

# 或者生成库
add_library(mylib STATIC math_utils.cpp)
# 使用CMake构建
mkdir build && cd build
cmake ..
make

这个完整的编译过程确保了C++源代码最终能转换为机器可以直接执行的二进制程序。