📖 第 2 章:基本概念(Basic Concepts)

🔤 Kenneth Reek《Pointers on C》(C和指针)· Chapter 2
🗨️ 本章金句:「源代码是给人看的文本,编译器眼里的 C 只是一串符合词法规则的字符流。」
这一章在干嘛? 回答两个根本问题:C 程序在什么「环境」里从文本变成进程?编译器按什么「规则」把字符切成 token?搞懂这两件事,很多玄学错误(多打个分号、注释吞代码)就不再是玄学。
2.1 环境:翻译与执行2.2 词法规则:字符、注释与标识符2.3 程序风格:写给三个月后的自己

2.1 环境:翻译与执行

翻译环境负责把源文件变成可执行程序,执行环境负责运行它。两套环境不必是同一台机器(交叉编译就是这么来的:在 PC 上编译,在单片机上执行)。

翻译的独立单位是翻译单元:一个 .c 文件 + 它递归包含的所有头文件。每个翻译单元独立编译成目标文件,再由链接器缝合。链接的本质是符号匹配:本单元引用的外部符号(extern 函数/变量)必须在别的目标文件里恰好有一份定义。

执行模型速写: 程序装载后从 main 开始(启动代码先把环境收拾好),main 返回值交给宿主环境表示成功/失败。局部变量住在栈上,static 变量和字符串常量住在静态区,malloc 的内存在堆上——这张地图第 11、18 章会展开。

2.2 词法规则:字符、注释与标识符

编译器看你的代码,第一步是字符集映射(换行符等统一化),第二步是切词(tokenize)。几条硬规则:

int x = 1;
int y = x/*ptr;   /* 想写 x / (*ptr)?糟糕! */
/* 上面这行 /* 之后全是注释:x/*p 被当成 x 和一个注释的开始,
   直到找到下一个 */ 才结束——经典的注释陷阱 */
警惕: 表达式里写 x/*p,除号会被「注释开始」吞掉。写成 x / (*p) 加上空格和括号,既安全又好读。

2.3 程序风格:写给三个月后的自己

风格不影响编译结果,但影响维护成本。本书的几条约定值得直接抄:

约定理由
一行一条语句,else 单独成行一眼看清控制流,diff 工具也友好
函数/变量名用小写+下划线或驼峰,宏全大写看到 ALL_CAPS 就知道这是编译期常量
指针变量名带 p/ptr 后缀或 ptr_ 前缀提醒使用者「这是地址不是值」,解引用前先三思
常量一律用 #define 或 const,不写裸数字改一处生效全局,还能顺手起个自解释的名字
一句话收束本章: 编译器按规则切词、按单元编译、按符号链接。理解规则本身,比死记错误信息有效得多。
🧠 小测验
1. 翻译单元是什么?它和源文件是什么关系?
一个 .c 文件加上它递归 #include 的所有头文件,展开后构成一个翻译单元,独立编译成一个目标文件。链接器再把多个目标文件按符号拼接成可执行程序。
2. 为什么 /* */ 注释不能嵌套?
编译器只匹配到第一个 */ 就认为注释结束。想注释掉一段本身含 /* */ 的代码时会提前截断,剩余文本被当普通代码处理,产生一堆莫名错误。可用 // 或条件编译 #if 0 替代。
3. a+++b 会被怎样切词?
按贪心策略切成 a ++ + b,即 (a++)+b,而不是 a+(++b)。切词只看 token 边界,与你的意图无关——所以别写这种需要人肉分析优先级的表达式。
← 上一篇🏠 顶层目录下一篇 →