工程101:理解指针

工程101:理解指针

如果你从 Python 或 JavaScript 开始写代码,指针听起来像是留着白胡子的系统工程师才懂的魔法。如果你在大学学过 C++,你记得和段错误还有内存泄漏搏斗的日子。

秘密是这样。指针只是一个变量。它的值是一个数字。那个数字存着某个东西的地址。

要理解指针,得先知道你的数据住在哪里。

两个领域:栈与堆

代码运行时,数据总得有个去处。它会落在栈(stack)或堆(heap)其中一方。

栈:又快又有条理

把栈想成桌上的一叠便利贴。

如果有个变量 int a = 10;,它通常会放在这里。又快又安全。

堆:西部荒野

堆是一座巨大的仓库。

所以,指针是什么?

指针就是一个路标。

如果 score = 99 这个变量在堆上(地址 0x1234),指针就是栈上一个很小的变量,上面写着"数据在 0x1234"。

int score = 99;  // 实际的值
int *p = &score; // 指针 'p' 持有 'score' 的地址

p 是地址。它告诉你数据在哪里。*p 是值,也就是那个地址里存的东西。

为什么要这样折磨自己?

为什么不直接把值传过去,让语言收拾善后就好?

1. 速度(不用复制)

想象一张 10MB 的图片。你把它传给 process(image) 函数,而你的语言把它复制一份,你就白白浪费了 10MB 的内存和复制像素的 CPU 时间。用指针的话,你只传地址(8 bytes)。函数知道图片在哪里,自己去看。

2. 共享状态

你把变量用值传给函数,函数收到的是副本。改副本不会动到原版。用指针的话,函数知道原版住在哪里,就能修改真正的数据。

3. 动态结构

链表、树、图,都是一块数据握着指向其他数据的指针。没有引用,很难做出这些结构。

要付出的代价

指针不是免费的。

  1. 内存开销: 指针本身就要占空间(64 位系统上通常是 8 bytes)。如果你有一个指向小整数的指针数组,花在指针上的内存可能比数据本身还多。
  2. 性能损失: "解引用"(沿着指针找到数据)要花一个 CPU cycle。也可能造成"缓存未命中"。如果数据离得很远,CPU 得等它被取出来。栈对缓存很友好。链表这种指针很多的结构,通常不是。
  3. 脑力负担: 空指针异常。悬空指针。内存泄漏。手动管理指针需要纪律。这就是为什么现代的语言(Rust、Go)或运行时(Java、V8)会想办法把指针藏起来或替你管理。

结论

指针是你的代码与硬件之间的桥梁。就算你写的是高级 JavaScript 或 Python,你碰到的对象底层都是引用(指针)。所以 a = b 会让 a 指向和 b 相同的数据。

© Melvin Laplanche - All rights reserved.