23.c-可执行程序.md 5.4 KB

.c → 可执行程序

判别规则(核心,唯一要记的)

  • # 的操作(include/define/ifdef)→ 预处理;语法/类型检查、C 转汇编 → 编译;生成 .o 机器码 → 汇编;跨文件符号合并、地址重定位、undefined reference → 链接

一句话:"# 开头的预处理,语法类型的编译,.o 的汇编,符号的链接。"

抽象描述(一句话本质)

.c → 可执行程序四步:预处理(文本处理)→ 编译(C 转汇编)→ 汇编(汇编转机器码 .o)→ 链接(合并 .o + 库 → 可执行)。每步只干自己那摊事,报错也按阶段定位。

四步流水线

hello.c
  → 预处理(cpp):展开 #include/#define/条件编译/删注释/加行号 → hello.i
  → 编译(cc):C 语言 → 汇编指令(语法/类型检查在这) → hello.s
  → 汇编(as):汇编 → 机器码 → hello.o(目标文件,还不能跑)
  → 链接(ld):合并 hello.o + 库/启动代码 → hello(可执行)
阶段 工具 输入→输出 干什么
预处理 cpp .c → .i 展开宏、包含头文件、条件编译、删注释、加行号
编译 cc .i → .s C 转汇编(语法/类型检查)
汇编 as .s → .o 汇编转机器码
链接 ld .o → 可执行 合并符号、地址重定位

报错定位(易混对比)

操作/报错 属于哪步
#include / #define / #ifdef 展开 预处理
头文件找不到(include 报错) 预处理
语法检查、类型检查 编译
#define MAX 1O0(字母 O)内容错误 编译(预处理只替换不查内容,编译解析时才报)
C 语言转汇编指令 编译
汇编转机器码(生成 .o) 汇编
符号合并、地址重定位 链接
undefined reference 报错 链接
内联函数展开 编译(优化阶段)

静态库 vs 动态库

静态库 .a/.lib 动态库 .so/.dll
链接时 用到的目标代码合并进可执行文件 只记录引用,不拷代码
可执行文件 变大
运行时 独立,不依赖库文件 依赖 .so 存在,由加载器运行时加载
更新库 要重新链接 换 .so 即可,不用重编译

变体验证(3 题,全过=学会)

  1. 陷阱题:.o 能直接运行吗 → 不能,缺启动文件、库、入口函数,要链接
  2. 陷阱题:#define MAX 1O0(字母 O)→ 编译阶段发现问题(预处理只做文本替换不查内容)
  3. 静态库 vs 动态库 → 静态库合并进可执行文件(变大);动态库只记录引用运行时加载(小但依赖)

口述要点(面试怎么讲)

  • 结论先行:四步(预处理/编译/汇编/链接),每步产物 .i/.s/.o/可执行
  • 地址重定位是什么:每个 .o 编译时从地址 0 开始编(不知道最后放哪),链接时把所有 .o 拼一起重新分配实际地址,把代码里跳转/访存的地方改成真实地址;extern 引用的符号在链接时填真实地址
  • 预处理还干什么:展开宏、包含头文件、条件编译、删除注释、添加行号标记(报错时定位文件第几行靠它)
  • 交叉编译:编译器本身在 x86 上跑,但目标平台设为 ARM,生成 ARM 指令的机器码,烧进 ARM 就能跑。关键在于编译器知道目标平台的字长、指令集、ABI(对齐、大小端)
  • 易错点:undefined reference 是链接错不是编译错;.o 不能运行;静态/动态库记反

关系网络

相邻概念 和本主题的关系 孤立理解会犯的错
extern undefined reference 就发生在链接阶段 以为 undefined reference 是编译错
程序分段 链接产物 = 分段图的基础(text/data/bss) 以为分段是编译期定的
内联函数 内联展开在编译优化阶段 以为内联是预处理/链接干的事
静态/动态库 链接阶段:.a 合并进程序,.so 运行时加载 把静态库/动态库记反

学习日期

2026-08-03


每主题一页,复习时只翻本目录。