gcc -S 生成汇编文件时用 .c 而非 .i,因 -S 自动完成预处理和编译;.s 文件含汇编指令、段声明、符号等,需检查 .globl main、终止指令及宏展开;加 -O 会大幅优化,如内联、删死代码;.s 中 call printf@PLT 仅为符号引用,链接时才校验定义。

gcc -S 生成汇编文件时为什么用 .c 而不是 .i?
直接用 gcc -S hello.c -o hello.s 就能生成汇编,不需要先跑 -E。因为 -S 会自动完成预处理 + 编译两步,再停在汇编代码生成阶段。如果你硬要拆开,得先 gcc -E hello.c -o hello.i,再 gcc -S hello.i -o hello.s——但多此一举,除非你真想 inspect 预处理结果。
生成的 .s 文件里有啥?怎么看它是否正常?
生成的 hello.s 是纯文本,含汇编指令(如 movq、call)、段声明(.text、.rodata)、符号(main:)、字符串字面量(.LC0:)等。可用 cat hello.s 直接看,或用 less hello.s 分页浏览。关键检查点:
- 有没有
.globl main或.text段开头 —— 没有说明预处理失败或源码没定义main - 最后一行是不是
ret或类似终止指令 —— 缺失可能意味着语法错误被忽略(比如漏了return但没开-Wall) - 字符串是否已展开(如
#define MSG "hi"→.string "hi")—— 不展开说明宏没生效,可能是#include路径错或宏定义被条件编译屏蔽
加 -O 会影响汇编输出吗?
会,而且影响很大。不加优化时,gcc -S hello.c 生成的汇编通常逐行对应 C 代码,变量存栈、调用全保留;加 -O2 后,常见现象包括:
- 函数内联:
printf可能消失,变成直接写寄存器 + 系统调用 - 死代码消除:未使用的变量、冗余计算整块消失
- 寄存器重用:局部变量几乎不进栈,全在
%rax、%rdi等寄存器里流转 - 跳转变少:
if可能编译成条件移动指令(cmov),而非jmp
调试时建议用 gcc -O0 -g -S;看性能优化效果,才用 -O2 -S 对比。
为什么链接时报 undefined reference,但 .s 文件里明明有 printf?
hello.s 里出现的 call printf@PLT 只是符号引用,不代表 printf 已打包进来。汇编阶段不查函数是否存在,只保证语法合法。真正校验在链接阶段:gcc hello.o 才会报 undefined reference to `printf'。所以:
-
gcc -S成功 ≠ 代码能跑 - 想提前暴露这类问题,加
-Wl,--no-as-needed或直接试链接:gcc hello.o -o hello && ./hello - 若用 musl 或静态链接,
printf可能根本不出现在 .s 里——它被替换成更底层的write系统调用
汇编文件只是中间产物,它不承诺可执行性,只承诺“这段汇编语法合法、符号名一致”。真正的依赖关系,得等到链接那一刻才落地。


















