过程调用与栈帧 / 调用约定
CPU 只有 32 个寄存器,但一段递归可以轻松嵌套一万层,每一层都有自己的一份局部变量、都记得该回到哪里去。这两件事摆在一起是有矛盾的:32 个格子,怎么装下一万层的现场? 答案不在硬件里。RV32I 里没有一条叫「call」的指令,也没有任何一条指令知道「函数」是什么。函数是约定造出来的幻觉——一份编译器之间互相签署的合同,加一块叫做栈的内存。这一篇就把这份合同逐条拆开。 1. 一次函数调用要解决四件事先别看汇编,想想 C 里这一行发生了什么: 1int y = f(x) + 1; 拆成机器视角,有四个独立的问题需要各自解决: 问题 说白了 谁来解决 去哪儿 PC 得跳到 f 的第一条指令 jal 指令 怎么回来 f 结束时得知道跳回哪一条 返回地址寄存器 ra 参数怎么递 x 得让 f 看得见 ABI 约定:a0–a7 寄存器归谁 f 里也要用寄存器,会不会把我的值踩了 ABI 约定:caller/callee saved 前两个由硬件(指令)解决,后两个纯靠约定——硬件完全不管你有没有遵守。这就是为什么手写汇编最容易死在后两条上。 ...
RISC-V 汇编基础
上一课我们把 C 语言的指针和位运算拽到了门电路旁边。现在还有一道缝没填上:sum += a[i] 这么一行字,CPU 里那几十亿个门到底是"照着什么"动起来的?答案是指令——一串二进制码,每一条都对应硬件里一小段被点亮的通路。汇编就是这串二进制的人类可读写法。 这一篇只干一件事:把 RV32I 这套指令集讲透到"你能手写一个循环、并且知道每条指令在硬件里意味着什么"的程度。 1. 指令集是硬件与软件之间的合同CPU 不认识 C,也不认识 Java。它只认识一件事:从内存里取一个 32 位的数,按事先约定好的规则解码,然后驱动相应的电路。这份"事先约定好的规则"就是 ISA(Instruction Set Architecture,指令集架构)。 ISA 规定了三样东西,一样都不能少: ISA 规定什么 具体内容 为什么必须由 ISA 定 有哪些寄存器 RV32I:32 个 32 位通用寄存器 编译器要知道往哪儿放变量 有哪些指令 add / lw / beq …… 二进制码怎么解码是...
数字逻辑与 C 语言回顾
不管你平时写 Java 还是 C,这台计算机底下其实没有任何"对象",只有一堆在 0 和 1 之间反复横跳的晶体管。CS61C 要做的,就是把"软件"一路拽回"硬件"的起点。这一篇干两件事:先用数字逻辑把"软件"接回"硬件"(门电路、加法器、补码),再系统回顾 C 语言里最容易踩的坑——指针、手动内存、位运算。 1. 什么是"位":一切从开关开始计算机最底层没有整数、没有字符串,只有一个个能表示两种状态的元件——通电 / 断电,记为 1 / 0,这就是一个 bit(位)。把若干 bit 并排,就能表示更大的数:8 个 bit 叫 1 字节(byte),32 个 bit 是常见 int 的宽度。 单个 bit 太弱,于是我们用"门电路(gate)"把 bit 组合运算。门是接受若干 0/1 输入、输出一个 0/1 的小电路,由晶体管搭成。最基础的几种: AND &...

