← 返回首页

C 语言开发环境与工具链笔记

课上讲"编译原理、调试机制与工程实践",听起来很抽象。落到具体的命令上其实就四件事:预处理、编译、汇编、链接。这四个词对应四个可以单独跑的命令,跑一遍就懂了。

从 .c 到可执行文件:四步

平时写 gcc hello.c -o hello,一条命令背后其实做了四件事。拆开看:

# 1. 预处理:展开宏、插入头文件、去掉注释
gcc -E hello.c -o hello.i

# 2. 编译:C 代码 -> 汇编代码
gcc -S hello.i -o hello.s

# 3. 汇编:汇编代码 -> 机器码(目标文件)
gcc -c hello.s -o hello.o

# 4. 链接:把目标文件和库合成一个可执行文件
gcc hello.o -o hello

中间产物各看一眼,很多"玄学"问题就落地了:

链接器到底在干什么

写过"未定义的引用(undefined reference)"报错的人都踩过这一脚:编译通过了,链接失败了。

原因很简单:编译阶段只检查语法和你写的声明,不关心函数到底存不存在;链接阶段才去找 printf 的真实地址。所以:

以后看到 "undefined reference to xxx",先想"我是不是没链对应的库",而不是怀疑代码写错了。

调试器:让程序慢下来给你看

加 -g 编译(保留符号信息),然后用 gdb:

gcc -g -O0 hello.c -o hello
gdb ./hello

(gdb) break main      # 在 main 处下断点
(gdb) run             # 跑起来,会停在断点
(gdb) next            # 单步,不进函数
(gdb) step            # 单步,进函数
(gdb) print x         # 看变量 x 的值
(gdb) backtrace       # 看调用栈
(gdb) continue        # 继续跑到下一个断点

段错误(segmentation fault)时最有用的是 backtrace——它会告诉你崩在哪一行、是谁调用的。比在代码里到处插 printf 高效得多。

工程实践上的几个习惯

一句话总结

编译通过 ≠ 程序正确。编译只保证语法对,链接保证符号齐,运行时的对错要靠调试器和测试来验证。搞清楚这三层的分工,大部分"为什么报这个错"就有答案了。