g++中“仅编译”(-S)与“编译并汇编”(-c)选项的区别是什么?
嘿,这个问题问到点子上了——很多刚摸编译流程的同学都会在「编译」和「汇编」这一步搞混,我给你一步步拆解清楚~
先理清整个编译链的顺序
首先回顾下g++的这几个选项对应的完整流程:
-E:仅预处理——把#include、#define这些预处理指令展开,生成纯C++代码(没有预处理指令了)-S:仅编译——把预处理后的高级语言(C++)翻译成汇编语言,生成.s后缀的文件-c:编译+汇编——先做-S的步骤,再把汇编语言翻译成机器能识别的机器码,生成.o后缀的目标文件
什么是汇编?
说白了,汇编语言是直接对应CPU指令集的低级语言,是人类能勉强看懂的“机器语言说明书”。每一条汇编指令,几乎都对应CPU能执行的一个基础操作(比如移动数据、加减运算、跳转)。
比如你写一句C++代码:
int sum = a + b;
编译后会生成类似这样的x86_64汇编代码(简化版):
movl -4(%rbp), %eax # 把变量a的值加载到eax寄存器 addl -8(%rbp), %eax # 把变量b的值加到eax里 movl %eax, -12(%rbp) # 把eax里的结果存到sum变量的内存位置
而汇编阶段,就是把这些人类能读的汇编指令,一对一翻译成CPU能直接执行的二进制机器码(比如movl对应一个特定的二进制 opcode),同时处理符号表(比如记录函数名、变量名对应的内存地址占位),最终生成.o目标文件——这个文件是二进制的,直接打开是乱码,但已经是机器能理解的指令了,只是还没把多个目标文件链接成完整的可执行程序。
编译和汇编的核心区别
我给你提炼两个关键差异:
- 处理对象与目标不同:
- 编译阶段:输入是高级语言(C++),输出是汇编语言。它要处理高级语言的语法、语义,做各种优化(比如循环展开、常量折叠),把C++里的类、函数、引用这些抽象概念,转化成汇编里的寄存器操作、内存访问、标签跳转。
- 汇编阶段:输入是汇编语言,输出是机器码(目标文件)。它几乎是“直译”——每一条汇编指令对应一个机器码,不需要处理高级语言的复杂逻辑,只负责把人类可读的汇编转成机器能跑的二进制。
- 与硬件的绑定程度不同:
- 编译阶段的前端(处理语法)是和语言绑定的,但后端(生成汇编)才和CPU架构绑定;
- 汇编阶段完全和CPU架构绑定——x86_64的汇编不能在ARM上汇编,反之亦然,因为不同CPU的指令集完全不一样。
举个实际操作的例子
你可以自己动手试一下:
- 写一个简单的
main.cpp:
#include <iostream> int main() { int a = 1, b = 2; std::cout << a + b << std::endl; return 0; }
- 用
g++ -S main.cpp生成main.s,打开这个文件就能看到完整的汇编代码; - 再用
g++ -c main.cpp生成main.o,用objdump -d main.o可以反汇编这个目标文件,看到里面的机器码对应的汇编指令(和main.s差不多,但已经是二进制形式了)。
内容的提问来源于stack exchange,提问作者pasha
相关产品推荐
相关产品推荐

