回到首页

输入关键词,多个关键词用空格分隔。

← 工程原理与设计 · 系列目录

从源代码到可复现修改:编译、Make 与 Git

约 3 分钟
本页目录

一次修改是否可靠,不只取决于代码看起来对不对,还取决于能否说明修改了什么、怎样构建,以及检查结果属于哪个版本。编译工具和版本控制各管一部分,连接起来才构成完整的工作过程。

本文合并重写自《Make 与 GCC 指南》和《Git 指南》,修正旧笔记中对警告、优化级别和暂存区的简化表述。

编译是几个阶段

C 程序通常经过预处理、编译、汇编和链接。以 hello.c 为例,GCC 可以分别停在不同阶段:

gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
gcc hello.o -o hello

-E 停在预处理,-S 生成汇编,-c 生成目标文件而不链接。日常也可以直接运行 gcc hello.c -o hello。详见 GCC 输出阶段说明。

macOS 上名为 gcc 的命令可能实际指向 Clang,检查 gcc --version 比只看命令名可靠。平台不同,目标格式和链接细节也会不同。

让编译器帮助发现问题

可以从 -std=c11 -Wall -Wextra -Wpedantic -g 开始建立练习环境。-Wall 启用一组常见警告,不是所有警告;-Wextra 补充更多检查。警告不是正确性证明,但不应该在没理解原因时直接关闭。见 GCC 警告选项。

优化级别也不是越高越好。性能要在实际工作负载中测量,代码体积、编译时间和可调试性都可能变化。调试与性能测试应该明确使用哪一组编译参数。

Make 记录依赖,不只是保存命令

保存下面内容为 hello.c:

#include <stdio.h>

int main(void) {
    puts("hello");
    return 0;
}

再保存一个 Makefile。命令行开头必须是 Tab:

CC = cc
CFLAGS = -std=c11 -Wall -Wextra -Wpedantic -g

hello: hello.o
	$(CC) $(LDFLAGS) hello.o $(LDLIBS) -o hello

hello.o: hello.c
	$(CC) $(CPPFLAGS) $(CFLAGS) -c hello.c -o hello.o

.PHONY: clean
clean:
	rm -f hello hello.o

运行 make 构建,运行 ./hello 应输出 hello。Make 根据目标和依赖的时间关系决定是否执行配方;项目增加头文件后,也要把头文件依赖纳入规则,否则修改头文件可能漏掉重新编译。

.PHONY 标记不对应实际文件的目标,避免一个名为 clean 的文件改变清理行为。更多规则见 GNU Make 手册。

Git 的三个观察位置

工作区是正在编辑的文件,暂存区是下一次提交准备采用的内容,HEAD 通常指向当前分支的提交。三者不同,所以检查修改也有不同命令:

git status --short
git diff
git diff --cached

git diff 通常比较工作区与暂存区,git diff --cached 比较暂存区与 HEAD。只看第一个 diff,可能漏掉已经暂存的修改。语义见 git-diff 文档。

用 git add hello.c Makefile 明确暂存范围,然后再次检查暂存内容。不要把“目录里出现了什么”直接等同于“这次都应该提交什么”。

一次修改怎样交接

先说明问题与预期行为,再完成实现和适当验证,最后检查差异中有没有临时文件、调试输出和不相关变化。提交说明写出为什么改,复杂修改再补充方案取舍。

构建成功只能说明当前环境中构建成功。要让别人复现,还应提供编译器、依赖、命令和必要输入。版本控制记录代码,构建配置记录方法,验证结果说明这次交付能支持什么结论。

需要展开查阅规则和操作时,可继续阅读Make 与 GCC 指南及Git 与 Vim 工作笔记。

相关文章