预处理、编译、汇编、链接详解

C/C++ 程序从源码到可执行文件,必须经过 预处理 → 编译 → 汇编 → 链接 四个阶段。下面我用最清晰、最底层、最工程化的方式,把每个阶段做什么、输入输出、核心功能讲透,带示例,让你彻底理解。

一、预处理(Preprocessing)

阶段:源码 → 预处理后的源码(.i 文件)
工具:预处理器(cpp)
核心:文本替换、宏展开、头文件展开、条件编译

预处理做什么?

1. 删除注释
2. 展开 #include 头文件(把 .h 内容直接插入)
3. 展开 #define 宏(文本替换)
4. 处理 #ifdef / #if / #else / #endif 条件编译
5. 添加行号、文件名信息(给编译器报错用)

示例

test.c

c 复制代码
#include <stdio.h>
#define MAX 100
int main() {
    int a = MAX;
    // 注释
    return 0;
}

预处理后 test.i(简化):

c 复制代码
  
// 一大堆 stdio.h 内容被插入
...
int main() {
    int a = 100;
    return 0;
}

总结一句话

预处理 = 无脑文本替换 + 条件裁剪

不做语法检查,不做类型检查,纯文本处理。

二、编译(Compilation)

阶段:预处理源码 → 汇编代码(.s 文件)
工具:编译器(cc1、g++)
核心:语法分析 → 语义分析 → 生成汇编

编译做什么?

  1. 词法分析:把代码拆成 token(关键字、标识符、符号)
  2. 语法分析:检查语法是否正确(生成 AST 抽象语法树)
  3. 语义分析:类型检查、函数声明检查、作用域检查
  4. 中间代码生成(IR)
  5. 优化(常量折叠、死代码删除、循环优化)
  6. 生成汇编代码(.s)

示例

test.i → 编译 → test.s(汇编)

asm 复制代码
  
main:
    pushq %rbp
    movl $100, -4(%rbp)
    movl $0, %eax
    popq %rbp
    ret

总结一句话

编译 = 把高级语言翻译成汇编语言 + 做大量优化

三、汇编(Assembly)

阶段:汇编代码 → 目标文件(.o / .obj)
工具:汇编器(as)
核心:把汇编指令翻译成机器码(二进制)

汇编做什么?

1. 把汇编指令 → 二进制机器码
2. 生成符号表(函数名、变量名 → 地址占位符)
3. 生成重定位信息(告诉链接器哪些地址需要修正)
4. 不解析外部符号(如 printf)

输出

test.o(二进制文件,不可直接运行)

总结一句话

汇编 = 翻译成人话就是"把文字指令变成 CPU 能懂的 01 串"

四、链接(Linking)

阶段:多个 .o 文件 + 库文件 → 可执行文件(ELF/PE)
工具:链接器(ld)
核心:符号解析 + 重定位 + 合并段

链接做什么?

1. 符号解析

找到所有未定义的符号(如 printf ),去库文件(libc.so、.a)找定义。
2. 重定位

把所有"占位符地址"替换成真实虚拟地址。
3. 合并段

把所有 .o 的 .text / .data / .rodata / .bss 合并成最终可执行文件的段。
4. 处理库

静态链接(.a):把库代码打包进可执行文件

动态链接(.so):只记录依赖,运行时加载

示例

test.o + libc.so → 链接 → a.out(可执行)

总结一句话

链接 = 把所有模块拼起来,把"未知地址"填成真实地址

五、四个阶段完整流程(最清晰总结)
plaintext 复制代码
  
test.c
   ↓ 预处理(#include、宏、条件编译)
test.i
   ↓ 编译(语法→语义→优化→汇编)
test.s
   ↓ 汇编(汇编→机器码→.o)
test.o
   ↓ 链接(符号解析+重定位+合并段)
a.out(可执行)
六、每个阶段的输入输出、工具、核心任务(表格版)
阶段 输入 输出 工具 核心任务
预处理 .c 源文件 .i 文件 cpp 头文件展开、宏替换、删注释、条件编译
编译 .i 文件 .s 文件 cc1/g++ 语法/语义分析、优化、生成汇编
汇编 .s 文件 .o 文件 as 汇编→机器码、符号表、重定位
链接 .o + .a/.so 可执行文件 ld 符号解析、重定位、合并段、处理库
七、最关键的底层概念(必须懂)

1. 预处理不检查语法
2. 编译不处理外部函数(如 printf)
3. 汇编只生成机器码,不填真实地址
4. 链接才是真正把所有模块"拼起来"的阶段
5. 可执行文件 = 链接后的最终产物

八、如果你想看真实命令(GCC 演示)

bash

预处理

gcc -E test.c -o test.i

编译(生成汇编)

gcc -S test.i -o test.s

汇编(生成.o)

gcc -c test.s -o test.o

链接

gcc test.o -o a.out

相关推荐
2301_789015625 小时前
C++:异常
开发语言·c++·异常·异常的处理方式
CVer儿5 小时前
c++接口内部内存分配问题设计
开发语言·c++
2301_789015625 小时前
C++:智能指针
c语言·开发语言·汇编·c++·智能指针
6Hzlia5 小时前
【Hot 100 刷题计划】 LeetCode 74. 搜索二维矩阵 | C++ 二分查找 (一维展开法)
c++·leetcode·矩阵
a里啊里啊5 小时前
常见面试题目集合
linux·数据库·c++·面试·职场和发展·操作系统
不想写代码的星星5 小时前
C++ 类型擦除:你对象是 Circle 还是 int 不重要,能 draw() 就行,我不挑
c++
是天创呀5 小时前
C++ 类核心知识总结
c++
鲸渔5 小时前
【C++ 基本数据类型】整型、浮点型、字符型、布尔型及大小
开发语言·c++
淀粉肠kk5 小时前
【C++】C++11 异常
c++