C 语言编译优化行为
更新时间:2026-08-26。本文是
languages/c/主题专家层的骨架文档(占位),完整展开将在后续批次补齐。
本文要回答的问题
-O0、-O1、-O2、-O3各做了什么?差别有多大?- 为什么
-O2下sum += i这种代码会被直接消掉? - 优化和
-g调试符号冲突吗?perf还准不准?
一、优化级别总览
| 级别 | 典型手段 | 代价 | 使用场景 |
|---|---|---|---|
-O0 | 几乎不优化,逐条对应源码 | 代码慢、体积大 | 调试、本站默认(保留符号与行号) |
-O1 | 常量折叠、基本块合并、死代码消除 | 编译变慢 | 快速验证 |
-O2 | 内联、循环优化、指令调度、向量化准备 | 编译更慢 | 生产默认 |
-O3 | 激进循环展开、函数版本化、更多向量化 | 体积膨胀、编译最慢 | 数值密集计算 |
二、常见优化手段速览
- 常量折叠与传播:
x = 2 + 3直接算出5;const值在编译期替换。 - 死代码消除:结果没人用的语句直接删掉——这就是
-O2下空转循环消失的原因。 - 函数内联:小函数调用点直接展开,省去调用开销,但符号表里函数可能"消失"。
- 循环优化:展开、强度削减、循环不变代码外提。
- 向量化:
-O3用 SIMD 指令一次处理多个数据。
三、与调试、剖析的冲突
-O2 会打乱"指令 ↔ 源码行"的对应关系:变量可能被优化没、函数可能被内联。此时 -g 的调试信息仍尽力映射,但 perf annotate 看到的汇编会和源码"对不上号"。本站默认 -O0 -g 正是为了保证剖析可读;想看优化效果时用 make release(-O2)对比。
四、与入门层的衔接
- 优化器消除空转循环的教学实验,见 for 循环 与 赋值与自增。
- 优化触发未定义行为的风险,见 未定义行为与优化。
- 用
-S看汇编对比优化前后,见 编译、链接与 ELF。
一句话总结
优化级别是"可读性 ↔ 性能"的旋钮:-O0 保符号、-O2 保性能,剖析时先 -O0 定位热点、再 -O2 验证效果。
本文为骨架文档:核心结构已就位,示例代码、反汇编对比与实测数据将在后续批次补齐。