计算机系统漫游:从比特到并发,构建底层认知的第一课

📅 发布时间:2026/10/22 4:37:05
计算机系统漫游:从比特到并发,构建底层认知的第一课
1. 先说清楚这门系统课第一站到底在讲什么CSAPP的第一章叫《计算机系统漫游》。很多人初看这一章会觉得过于宽泛又是信息表示又是编译流程又是硬件又是操作系统像一篇大杂烩。但我作为写了好几年业务代码、回头补系统底层的过来人想认真说一句这一章值得仔细读它是整个系统世界观的第一块地基。这一章不要求你立刻掌握任何硬功夫。它要做的恰恰是把你带入系统视角一个程序从敲下源码到屏幕上出现输出中间跨越了哪些阶段硬件在背后做了什么操作系统在中间扮演什么角色网络如何把单机变成系统之系统。如果你能顺着这条线完整走通一遍后面学链接、异常控制流、虚拟内存、网络编程这些硬核章节时就会清楚每块拼图应该放在哪里。这本书在业内口碑极高几乎所有认真学过系统知识的人都会把它列为必读书目。第一章是全书提纲挈领的总览作者刻意用最简单的hello程序当线索把信息表示、编译、硬件组织、缓存、操作系统抽象、网络、并发并行这些主题全部串一遍让读者在进入细节之前先看到整个领域的全貌。无论你是在校生准备补系统基础还是工作后想回头填坑这一章都是一个非常好的起点。1.1 我为什么在写了很多年代码之后回头补这门课我自己的经历比较典型。前几年写业务系统日常主要是高级语言和框架CRUD写得滚瓜烂熟但一遇到性能问题就感觉隔了一层纱。某个接口慢不知道是该查索引、调缓存还是改并发模型线上偶发诡异崩溃脑子里没有栈、堆、内存映射这些概念排查全靠瞎试。后来我意识到缺的不是某一份具体技巧而是一套统一的底层模型。CSAPP这类课程的定位正是搭建这套模型它不是教你怎么写更漂亮的业务逻辑而是告诉你代码最终落到硬件上如何运转操作系统如何欺骗你的程序性能瓶颈到底卡在哪一层。补完之后回头看业务问题原来很多玄学都有了清晰的解释路径。1.2 第一章在全书的定位一张地图不要求立刻全懂第一章是全书的导览而不是真正教学的开始。它带着你把整个系统漫游一圈每个主题都是惊鸿一瞥说信息用位表示但不深入编码细节说编译系统但不展开语法树说缓存但不讲替换策略说进程但不谈具体接口。这些细节在后面的章节里会被一一展开第一章只是让每个概念先露个脸。所以读这一章千万不要背着必须全部记住的包袱那是错误读法。正确的做法是建立关键术语的印象理解整个系统的宏观流程然后在后续章节里反复回来对照。我自己第一遍读的时候DMA、虚拟地址、上下文切换这些词都只是混了个脸熟真正理解都是后面学完对应章节回看时才完成的。遇到不懂的内容做标记、放过去、继续往下走比原地死磕强得多。2. 一切信息都是位上下文这条底层铁律的实战意义系统里所有信息无论是磁盘里的文件、内存中的程序和数据还是网络上传输的数据包本质上都是一串比特bit。比特本身没有含义含义来自你怎么解读它。这是第一章开篇就强调的核心观点也是一把钥匙能解释很多让人困惑的现象。书中把这句话浓缩成信息位上下文。同一串字节在不同解释规则下可以是整数、是字符、是一条机器指令也可以是图片的一部分。区分它们的不是字节本身而是你所在的上下文。这句话我后来在工作中反复验证几乎每一类数据变乱的问题根源都能归到这个公式上。2.1 同一个字节换一个上下文就完全变味举一个最简化的例子数字0x41也就是二进制01000001。把它当成ASCII码解释它是大写字母A把它当成整数解释它是十进制的65把它当成机器指令的一部分它可能是一个寄存器操作指令的操作码。你以为是数据的东西机器可能正在把它当指令执行。这种误解轻则报错重则引发缓冲区溢出之类的安全问题。我在处理乱码问题时就切身吃过这个亏。一段字符串在某个环境里变成满屏问号或者奇怪的符号本质就是解释规则对不上同一串按UTF-8编码的字节被当成另一套老编码去解码自然驴唇不对马嘴。以前遇到乱码我全靠猜不是试各种编码就是查各种资料自从理解了位上下文第一反应就变成了确认这个字节流在各个环节分别被谁、按什么规则解释过。2.2 这条铁律在调试和编码时的直接体现落到实操层面这条铁律给了三个非常明确的启发我现在排查问题基本都按这个思路走第一排查编码类问题先确认两端用的是同一套解释规则。文件、数据库、网络协议、终端每一个环节都可能重新解释字节任何一个环节的规则不一致数据就会变味。先梳理完整链路再谈修复比盲目转换编码高效得多。第二看十六进制永远不会骗人。当程序某段数据混乱时把它按字节dump成十六进制再和期望值对比通常能快速定位是哪一层解释错了。乱码、协议字段错位、文件头损坏这些场景里这一招尤其好用。第三也是容易被忽略的代码和数据在内存中都是字节底层并没有天然划分哪段是指令、哪段是数据全靠上层约束。所以只读数据被篡改、函数指针被改写这classic攻击手法能成立根源就在这。理解了这一点你会对内存安全心生敬畏写代码时也会更谨慎地处理边界。3. 从源代码到可执行文件编译系统的四个阶段谁也别想跳过一个平平无奇的hello.c源文件要变成可执行文件在主流的开发工具链下要经历四个阶段预处理、编译、汇编、链接。每一步都有对应的工具和产物任何一步出问题最终程序都跑不起来。这一节是第一章里我最建议大家反复看的部分因为它和日常开发的关系实在太紧密了。下面这张表把四个阶段的输入、输出和主要行为列清楚了建议收藏备用阶段输入主要动作输出典型产物特征预处理hello.c展开#include、处理宏、删注释hello.i仍是文本但体积膨胀编译hello.i语法语义分析、生成汇编、做优化hello.s汇编代码可读汇编hello.s把汇编翻译成机器指令hello.o二进制可重定位目标文件链接hello.o 依赖库合并目标文件、解析符号引用hello完整可执行文件多数人在大学里也学过这四步但基本是背概念考完就忘。我想说的是这四个阶段不是书上的冷知识它们离日常开发近得惊人。理解了每个阶段干什么你才能快速判断编译器报错到底是谁抛出来的。3.1 预处理、编译、汇编、链接都干了什么以最简单的hello.c为例#include stdio.h int main() { printf(hello, world\n); return 0; }预处理阶段做的是文本层面的处理把#include的stdio.h内容真正插入进来展开所有宏定义删除注释。这个阶段的产物还是人可以读的文本你可以用gcc -E直接查看展开后的样子。我第一次看的时候挺震惊的一个几行的程序展开出来能到好几百行全是各种声明和类型定义这还没算上递归包含带来的爆炸。编译阶段把C代码翻译为汇编代码。这里会做语法检查、类型检查还会做大量优化。用gcc -S可以生成.s文件里面是一行行给汇编器看的指令文本。如果你从未看过汇编第一次看会有点懵但大致的load、add、store这些助记符是能认出来的。汇编阶段把汇编文本变成机器指令生成.o文件。这个文件已经是二进制了但还不能单独运行因为它引用的printf符号还没有定位——它得和标准库里的实现绑定在一起。用工具查看.o文件时你能看到它带着一堆符号表信息等着被链接器整理。链接阶段干的就是最后一公里把编译出来的目标文件和依赖的库合并在一起解析符号引用最终生成一个完整的可执行文件。开发中大量undefined reference报错就是在这里发生的。3.2 理解四个阶段对排查编译错误的实战价值我现在排查编译相关问题第一反应是先判断错误发生在哪个阶段因为不同阶段的报错长得完全不一样排查思路也不一样语法错误和类型错误基本发生在编译阶段。报错信息里通常带着文件行号比较好定位比如漏写分号、函数签名不匹配、数组越界编译期可查等。符号解析类错误比如undefined reference to xxx发生在链接阶段。这类错误经常和静态库的链接顺序有关系。链接器处理静态库时是顺序扫描的如果你把库放在依赖它的目标文件前面符号就可能找不到这就是为什么有经验的开发者总强调把库写在依赖它的文件后面。很多新手被这个坑折磨过本质原因就是不知道这是链接阶段的规则。还有一类multiple definition错误是两个目标文件定义了同一个全局符号链接器不知道该用哪个。以前我看到这类报错还会慌现在知道它发生在链接阶段后就知道该去查符号重名和重复包含问题。知道阶段划分之后你会本能地按这句话是谁说出来的去筛选报错信息排查速度快很多这是第一遍读这章最直接的收益之一。4. 硬件舞台怎么转总线、主存、处理器和那台hello的执行理解了程序的编译旅程接下来看执行旅程。第一章把计算机硬件组织拆成四个主要部分总线、I/O设备、主存、处理器。它们的分工极其明确配合起来像一条精密流水线。别嫌这部分讲得浅这是你第一次真正看清程序在硬件上是怎么活的。总线是贯穿系统的数据通路负责在组件之间搬运字节。它搬运的数据量通常以字word为单位。32位系统一次最多传4字节64位系统翻倍到8字节这也是位宽直接影响系统上限的原因之一。去服务器上看CPU和内存的架构时你会频繁听到总线带宽、内存通道这些词背后都是这个基础概念。I/O设备是系统与外部世界的接口键盘、鼠标、显示器、磁盘都属于这一类。最初级的操作方式是处理器亲自处理每个外部字节的输入输出效率惨不忍睹更聪明的做法是用设备控制器辅助再配合中断机制让处理器不用傻等外部设备。每次按键、每次磁盘请求都触发中断操作系统在中断处理程序里完成真正的工作这个机制在后面的异常控制流章节会详细展开。主存是程序运行时的临时存储区物理上由DRAM这类易失性存储器构成。它只管暂时存放指令和数据断电即失所以不能当持久化存储用。主存的特点很矛盾容量比寄存器大得多但速度比寄存器慢好几个数量级这就为后面讲缓存埋下了伏笔。处理器是真正执行指令的大脑它靠一个程序计数器PC决定下一条指令在主存中的位置。从通电那一刻起处理器就陷入一条永不停止的循环取指、解码、执行、更新PC。这个模型朴素得不像现代科技的产物但几十年来的通用处理器不管内部多复杂底层思想仍然离它不远。4.1 处理器的取指-执行循环用第一章的说法处理器执行的指令模型非常简单从PC指向的内存位置读指令解释其中的位模式执行指令要求的操作然后更新PC使其指向下一条指令。它可能做的事情包括加载从主存读数据到寄存器、存储从寄存器写数据到主存、运算对两个寄存器里的值做算术、跳转修改PC实现流程控制。值得反复体会的是存储指令和存储数据使用的是同一块主存。指令在磁盘上、在主存里、在被CPU读出之后都只是一堆字节。这跟前面说的位上下文完全呼应同一段内存被PC取出来就是指令被数据访问指令取出来就是数据。很多安全漏洞能利用的就是这种形态模糊。我第一次看这个模型时最大的震撼是原来程序不是被运行在某个神秘空间里的它就是一坨躺在内存里的字节处理器逐条把字节当成指令处理仅此而已。把这个模型牢牢刻在脑子里后面对栈帧、指令流水线、分支预测的理解都会顺很多。4.2 一条真实路径从键盘回车到屏幕输出第一章最精彩的部分是用hello程序把刚才所有硬件串成一条完整链路。过程大致是这样的用户在键盘上输入./hello后shell程序把hello可执行文件从磁盘读入主存。这里有个关键细节主存和磁盘速度差距极大现代系统不会让处理器逐字节去磁盘取数而是利用DMA直接存储器访问机制让磁盘控制器直接把数据块搬进主存搬完再触发中断通知处理器。整个过程处理器基本没插手大块数据搬运效率拉满这是第一章为数不多值得先记住的硬件细节之一。然后处理器开始执行机器指令把hello字符串从主存复制到寄存器再写入显示设备的寄存器最终显示器把这些字节渲染出来屏幕上出现了hello, world。翻回头再看这条链路是键盘输入→磁盘→DMA搬运→主存→处理器执行→显示设备。输入设备、总线、主存、处理器、输出设备每个角色都上场了一个不落。这一小节读起来轻描淡写但它是全书第一个系统全景。后续学虚拟内存时会发现实际加载过程还要经过更复杂的地址映射但宏观路径不会变。先把这条路径钉在脑子里后面所有细节都往这个框架里填。5. CPU很快但存储跟不上高速缓存与存储层次的真相从上面那条执行路径里能隐约感受到硬件各部件之间存在巨大的速度差。第一章把这层窗户纸捅破了处理器和主存之间的速度鸿沟大得离谱而这个鸿沟正是靠高速缓存填平的。缓存不是优化技巧是绕不过去的系统现实。5.1 寄存器、SRAM、DRAM的速度差到底有多大书中给过一组让人印象深刻的对比CPU寄存器或者离它最近的L1缓存可能在一个时钟周期内就能完成访问而主存的访问延迟可能达到几十上百个周期。这个差距不好想象我帮你放大一下如果CPU的一次操作耗时一秒那么访问主存就像等待一分钟而访问磁盘则像等了好几个小时。高速缓存就是为弥合这道鸿沟而存在的。它在物理上非常靠近处理器用更快的SRAM实现。现代系统普遍采用多级缓存层次L1紧贴核心、容量最小但最快L2次之L3在多个核心之间共享再往下才是主存。每一级都比下一级快、比上一级小、比上一级贵形成一条完整的存储金字塔。缓存之所以有效靠的是一条名为局部性的规律。这条规律不是人造的而是程序天然的行为特征几乎所有的计算都倾向于反复使用最近用过的数据以及它附近的数据。第一章没有讲缓存替换算法但局部性的概念已经足够指导你的编程习惯。5.2 局部性原理在真实代码里的体现局部性分两种。时间局部性刚访问过的数据短时间内很可能再次被访问典型的例子是循环里的计数器。空间局部性访问某个数据后它周围地址的数据很可能也会被访问典型的例子是顺序遍历数组。我第一次真正体会到局部性的威力是在优化一段矩阵运算代码的时候。用两层循环遍历一个大矩阵一种写法是外层行、内层列另一种是外层列、内层行两者访问内存的顺序完全不同运行速度能差出好几倍。原因就是前者按行顺序访问对缓存极其友好后者每一步都跳去很远的内存位置缓存几乎无可利用只能频繁回主存取数。从那以后我写代码时特别关注访问模式遍历数组尽量按存储顺序走处理结构体时尽量把一起用的字段放一起把内层循环里最热的变量控制在能进寄存器或L1的程度。第一章讲缓存不是让你去背缓存参数而是让你建立一个习惯多想想自己的访问模式是否连续、是否在重复利用最近读到的数据。这个习惯值多少钱等你遇到真实性能瓶颈就知道了。存储层次的完整图景是这样的寄存器最快最小→ 多级缓存 → 主存DRAM→ 本地磁盘 → 远程存储。越往下容量越大、单价越低、访问越慢。有趣的是每往下一层都可以看作上一层的一个缓存缓存是主存的缓存主存是磁盘的缓存本地磁盘是远程存储的缓存。这个递归思路等学到虚拟内存时会再次出现。6. 抽象层与操作系统三大件进程、虚拟内存、文件硬件讲完第一章立刻转入软件视角操作系统。没有操作系统应用程序面对的是裸机那样的世界没法想象每个程序得自己管磁盘读写、管内存分配、管设备驱动还要防止别的程序搞乱自己的数据。操作系统夹在硬件和应用之间干的就是这个脏活累活。第一章把操作系统的职责归纳成两层一是防止失控应用滥用硬件二是为应用提供一套简单、统一、与硬件无关的抽象。整套抽象体系里最核心的就是三大件进程、虚拟内存、文件它们分别抽象了处理器、主存和I/O设备。可以说操作系统开发者每天面对的概念归根结底就是这三个。6.1 一次性讲清进程切换为什么让每个程序都觉得独占CPU进程这个概念的核心作用是给每个正在运行的程序一个错觉CPU好像被你一个人独占。实际上处理器需要在多个进程之间快速切换每次切换都要保存当前进程的上下文再恢复另一个进程此前保存的上下文这个动作叫上下文切换由操作系统内核完成。这个过程很像多人在一台电脑上轮流用键盘每个人以为自己一直在用键盘实际上是操作系统在极短的时间片之间来回切换。切换不是没有代价的保存和恢复寄存器状态会刷新流水线和缓存所以进程切得越频繁系统开销越大。做高并发服务的同学应该深有体会线程过多导致上下文切换成为瓶颈吞吐量反而上不去。这就是第一章讲的底层知识在高并发设计里的直接回响。真正把进程机制讲透要等后边的异常控制流章节第一章只需要你建立这个感觉进程不是天然存在的实体它本质上是一种抽象是操作系统精心制造的幻觉。理解了幻觉二字后面的进程控制和信号处理学起来会顺很多。6.2 虚拟内存让我们用连续地址骗过硬件虚拟内存抽象让每个进程都以为自己独享一片从0开始、连续、私有的大地址空间而实际物理内存是被所有进程共享的。进程看到的是虚拟地址硬件访问的是物理地址这中间靠地址翻译和页表机制完成映射属于后面章节深挖的内容第一章只需要建立虚拟地址≠物理地址的直觉。这个直觉带来的认知冲击是巨大的。程序里打印出来的指针值并不是真实的物理地址你完全不能靠它猜测数据在内存条上的物理位置。进程之间的地址空间相互隔离你不能通过别人的指针访问别人的内存所谓段错误很大程度上就是虚拟地址访问越界被操作系统拦下来了。理解虚拟内存的最大价值在于建立对进程隔离和内存保护的信任基础。你用的每次malloc、每次数组访问会不会引发灾难都和虚拟地址到物理地址的映射规则有关。等学到虚拟内存章节时你会发现第一章这句话背后的实现复杂程度惊人但现在先记住结论就够了。6.3 文件抽象一切输入输出都是字节流文件抽象是三大件里最好理解、影响也最深远的一个。它把磁盘、键盘、显示器、网络接口等各种各样的I/O设备全部统一成字节流。应用程序只需要open、read、write、close这一套接口至于背后是真实的磁盘文件还是某个特殊设备应用通常完全不关心。这个抽象让各种I/O之间可以互相替代也催生了一切皆文件的设计哲学。终端可以被当成一个文件来读写信令网络连接可以被当成文件描述符来操作设备驱动暴露成/dev下的特殊文件。理解了文件抽象再看那些把socket也当文件处理的对象就一点都不觉得奇怪了。对应用层开发者来说文件抽象的另一个价值是统一异常处理路径一切I/O都归结为打开、读写、关闭三个动作查错路径也统一了。后面学到文件系统章节时你会看到文件抽象之下的inode、目录项等实现细节但那个层次只属于系统开发者普通应用只需要牢牢站在抽象之上。7. 网络把单机变成系统之系统第一章的倒数第二块拼图是网络。网络的存在让hello world不仅能跑在本地还可以通过消息传递跑在另一台机器上。整个互联网由此可以被看作一个超大计算机系统这也是系统之系统说法的来源。网络不是单机系统的附加品它本身就是系统思想的一部分。书里的例子是这样串起来的你在本地机器上打开一个远程终端向远程机器发送运行hello的指令。这条指令先是作为文件字节流从本地键盘读入经本地操作系统打包成网络消息由网络适配器发送出去远程机器收到消息后把其中的数据字节交给远程shell远程shell再把hello程序加载执行最后把运行结果原路返回。7.1 一次远程hello的完整链路拆开看这条链路每一步都在前面见过键盘输入对应I/O设备读入对应DMA搬运和文件抽象打包发送对应协议栈处理远程加载执行对应编译与加载流程。这个例子的巧妙之处在于它把第一章讲过的所有概念无缝串了起来还额外补充了网络这一环。对于做开发的人来说网络不只是发个请求收个响应这么简单。消息在传输过程中会经过多层的封装和解封装每一层都有它的职责边界哪一层负责拆包、哪一层负责路由、哪一层保证可靠传输。第一章不需要你掌握这些细节但需要你意识到网络通信和本地文件读写本质上都建立在字节流之上抽象思路是同一套。7.2 系统之系统对学习路径的启发我在读完第一章之后对网络章节的第一个直观感受是它不再是孤立的技术点而是整个系统知识拼图里自然延伸出去的一块。单机系统管的是如何让一个进程跑起来网络系统管的是如何让多个进程跨机器协作底层逻辑是一致的。这个系统之系统的观点还有一个实际用途排查网络问题时不要把自己局限在网络层一定有错的预设里。很可能问题出在本地文件描述符耗尽、对方进程崩溃、双方字节序不一致甚至是某个抽象层的配置错误。带着系统全局观去排查永远比对着单一协议栈瞎猜高效。8. 并发与并行三个不同层次的加速机会第一章最后的技术主题是并发与并行。这是两个经常被混用的词但它们是两个完全不同的维度并发指多个任务在时间上有重叠不强调同一刻真的同时执行并行要求真正的同时执行依赖多核等硬件能力。打个比方并发是一个人在多任务之间来回切换并行是多个人同时各干各的。掌握了这个区别很多技术决策就有了方向。单核CPU也能支持并发靠的是时间片轮转但只有多核多机才能谈得上真正的并行。你写多线程程序是在搞并发编程但它到底能不能转成并行执行取决于底层有没有并行资源。这两句话想通了很多面试题也自然通了。8.1 并发不是并行这两个概念我见过一堆人搞混我面试时经常会抛这个问题你解释一下并发和并行的区别。得到的答案五花八门混着说的占了一大半。最简明的区分是并发是程序结构层面的性质是逻辑上的同时并行是执行资源层面的性质是物理上的同时。一个单核CPU照样能并发运行多个线程但只有多核CPU或多机集群里才有真正的并行。搞混这两个概念最直接的后果是设计系统时定错技术方案。比如有人以为加了多线程就一定能吃满CPU结果程序大部分时间在等锁、等I/O多线程变成负优化还有人以为加几台机器就能解决所有并发问题结果因为数据一致性、分布式锁等问题反而更复杂。正确的思路是先理清并发模型再考虑如何通过并行资源加速顺序不能反。第一章在这里其实是给全书的性能优化主题预热。真正动手做并发编程要等到进程、线程章节但这里建立的并发是结构、并行是资源的直觉会伴随你整个系统学习旅程。8.2 从线程级到指令级再到SIMD三个层面加速第一章把并行划分成三个层次每一层对写代码都有实际启发线程级并发是通过多线程或多进程共享单个CPU核心的执行能力或者在多核处理器上真正并行执行。这是后端开发里最常打交道的层面线程池、协程、进程模型都归于这一类。指令级并行是现代处理器的内部魔法一个时钟周期内可以同时执行多条指令靠流水线和乱序执行让多条指令部分重叠。这一层通常不需要程序员手工干预编译器和你选的优化等级会发挥很大作用你需要做的往往是别写出阻碍指令级并行的代码比如避免不必要的分支依赖。SIMD单指令多数据并行是一条指令同时操作多份数据比如一次处理4个浮点数。在图像处理、音视频编解码、科学计算这类循环密集场景SIMD优化能带来数量级的提升。现代编译器对简单循环的自动向量化做得不错但复杂场景还是得手工写内建函数。了解这三个层次的先后顺序很重要先保证线程级并发设计合理再考虑指令级和SIMD。很多人一上来就钻研SIMD指令忽略了更大的设计瓶颈属于用力方向错了。第一章把这些概念一摆至少你在后续章节遇到并行这个词时脑子里能立刻分出几个层面来。9. 第一遍读完之后的实操建议第一章的内容其实一个晚上就能读完但读完之后怎么内化我有一些个人经验分享。第一一定要亲手把hello全流程跑一遍。在本地写个hello.c编译出来执行再挨个查看预处理产物、汇编产物、目标文件符号表、运行时的依赖列表。这一套流程比看十遍文字都管用它把抽象概念全部落到你眼前能看到的文件上以后别人跟你说预处理、链接顺序你脑子里有具体画面。第二把第一章讲过的主题画一份自己的连线图。我会用一张白纸从hello.c出发画出编译四阶段、硬件组件分工、操作系统三大件抽象、网络链路用箭头标数据流方向。这张图不需要专业它是你自己的认知地图后续每学一章都可以回来补充分支。我至今保留着这份手绘思路图学到虚拟内存和网络编程时都在上面新增了节点。第三不要太早铺开买一堆参考书。第一章涉及的概念在后续章节都会深入现在只需要做到看见名词不陌生、能讲清大概。以进程为例第一章只讲抽象概念后面会有整章覆盖进程的创建、控制与信号到时候再抠细节也来得及。提前铺得太开反而容易把精力耗散。第四如果发现编译、链接相关章节吃力建议先回头补C语言基础尤其是指针、数组、函数调用这些内容。全书默认读者会C语言基础不牢后续读起来会非常痛苦。反过来如果你本来就有C经验这章会读得飞快甚至是纯粹的享受。这一章很多人读得轻轻松松因为它没有复杂公式没有艰深算法。但我可以负责任地说它是全书性价比最高的一章花一个晚上建立全景认知之后所有章节的坡度都会因此平缓不少。等到学完存储层次、虚拟内存、网络编程再回头看这一章你会发现当初那些轻描淡写的句子字字都是伏笔。把这些线索收好接下来往深处走就好走多了。