⚙️ 第一章 对程序员来说,CPU到底是什么
📄 一句话概括

数组取地址只有一条公式——元素地址 = 基址 + 序号 × 元素长度;两个寄存器各管一段。

📝 本节和前面几节的关系

1.5 讲了栈(一段内存按后进先出用),这一节讲另一段最常用的内存用法——按序号直取。

一、数组在内存里就是排好队

数组的元素一个挨着一个放,而且每个占同样多的字节(同样类型)。 这两条一凑,就能算出任意一个元素在哪:知道开头、知道每个多长就够了。 公式写出来只有一行:元素地址 = 基址 + 序号 × 元素长度。 例:int 数组从 0x1000 开始,每个 int 占 4 字节,那 a[3] 在 0x1000 + 3×4 = 0x100C。

二、两个寄存器各管一半

CPU 为这件事准备了两个寄存器(1.2 提过): 基址寄存器装【数组从哪开始】——0x1000。 变址寄存器装【要第几个】——3。 CPU 在一条指令里把两者加起来(有的架构的寻址方式还自带乘 4)。 所以人写 a[3],机器看到的是【基址 + 3×4】——这个换算不用你操心。

三、为什么要乘,不能直接加

因为单位和序号不是一回事:地址的单位是字节,序号的单位是元素。 元素占几个字节,就要乘几——这就是那个乘法的全部来历。 顺带解释了一个老问题:为什么数组下标从 0 开始? 因为0 号元素的地址恰好就是基址本身,不用做任何减法——这是最省事的起点。

四、换个写法就是指针

指针存的也是地址,本质上就是【把基址放进一个变量】。 所以 a[i] 与 *(p + i) 是同一件事的两种写法:一个让机器算、一个你自己算。 理解到这一层,指针的神秘感就没了:它不特殊,它只是一段门牌号。

五、动手 Demo:基址 + 变址,算出数组元素的地址

内存像一幢大楼:每个字节是一间房,地址就是门牌号。 数组的取地址公式只有一条:元素地址 = 基址 + 序号 × 每个元素占几个字节。

base, size, a = 0x1000, 4, [10, 20, 30, 40, 50]
for i, v in enumerate(a):
    print("a[%d]  基址 0x%04X + %d x %d = 地址 0x%04X  值 %d"
          % (i, base, i, size, base + i * size, v))

实跑输出:

--------------------------------------------------------------------
基址 + 变址,算出数组元素的地址
--------------------------------------------------------------------

内存像一幢大楼:每个字节是一间房,地址就是门牌号。
数组的取地址公式只有一条:
  **元素地址 = 基址 + 序号 × 每个元素占几个字节**

  样本:int a[5],从地址 0x1000 开始,每个 int 占 4 字节

  序号 i     基址寄存器     变址 i×4     算出的地址     内存里的值
  -----------------------------------------------------------------
  0          0x1000         0            0x1000         10
  1          0x1000         4            0x1004         20
  2          0x1000         8            0x1008         30
  3          0x1000         12           0x100C         40
  4          0x1000         16           0x1010         50

  三个角色各干一件事:
    基址寄存器  装着 0x1000——数组开头在哪儿
    变址寄存器  装着 i——要第几个
    CPU         顺手做 base + index × 4

  所以你是**不用**自己算地址的:写 a[3],机器替你做了上面这道乘法加法。
  **人看到的是【第几个元素】,机器看到的是【哪一号门牌】。**

基址管开头、变址管第几个,CPU 顺手做一次乘加。 人看到的是【第几个元素】,机器看到的是【哪一号门牌】。

30 秒实验

  1. 用 Python 打印一个整数列表各元素的地址(或用 id() 观察),看相邻元素差多少。
  2. 手算:一个 int 数组从 0x1000 开始,a[7] 的地址是多少?

常见误区

  1. 以为数组【知道自己的长度】。内存里的数组只知道开头在哪,长度是语言层面替你记着的。
  2. 以为指针是另一个物种。它和你算的地址是同一类东西,只是一个存在变量里。
自测三问(点开对答案) 1. 数组元素的地址公式是什么? —— 元素地址 = 基址 + 序号 × 每个元素占的字节数。 2. 基址寄存器和变址寄存器各装什么? —— 基址装数组开头的地址,变址装序号(偏移量)。 3. 为什么数组下标从 0 开始最省事? —— 0 号元素的地址就是基址,不需要额外做减法。

相关笔记