引用与指针
复习
- 数组:连续存储,按下标随机访问
- 抽象数据类型:接口与实现分离
- 程序在内存里的样子:代码、数据、栈和堆
TL;DR
- 指针/引用保存的不是数据本身,而是数据所在的位置
- 通过它,可以在别处访问同一块数据
- 悬空引用指向已被释放的空间,非常危险
- 有了指针,数据就不必再连续存放
正文
数组靠“连续”换来了随机访问。可现实中的很多数据,天生就是零散的,硬要挤在一起既不方便,也做不到。要想把它们串起来,就需要一样新工具:指针。
保存“位置”,而不是“内容”
指针(pointer)保存的,不是数据本身,而是数据所在的地址。
它和“引用”(reference)是近亲:引用通常是指针的一层友好封装,用起来更像是“数据的别名”。
打个比方:房间号不是房间本身,但它能带你找到那个房间;书签不是书,但它指出了你读到哪一页。指针就是内存世界的“房间号”或“书签”。通过它,你能在程序的一个地方,访问到存放在另一个地方的数据。
有了它,数据不必连续
指针最深远的贡献,是解除了“连续性”的束缚。
数据可以零零散散地放在内存各处,只要每个数据里保存着“下一个数据在哪里”,就能把它们连成一串。这样一来:
- 插入、删除不必再像数组那样整体挪动,只要改几个指针即可
- 数据不必挤在一段连续空间里,更容易动态分配
这为下一章的链表铺平了道路:链表正是靠指针,把分散的节点连了起来。
强大,也危险
指针给了程序很大的自由,也带来了著名的麻烦:
- 空指针:指向“什么都没有”,一旦使用就会出错
- 悬空指针:指向的内存已经被释放,可指针还留着,再去访问就访问到垃圾或别人的数据
- 越界/野指针:指到了不该指的地方
其中悬空指针尤其阴险:它平时可能“看起来正常”,直到某天才突然出错,而且很难复现。自由越大,责任越大——这也是一些新语言选择把指针“藏起来”、只提供安全引用的原因。
理解了指针,就可以看看用它在数据之间“牵线搭桥”的第一个结构了。
思考题 1
指针保存的是数据本身,还是数据的位置?这带来了什么灵活性?
思考题 2
悬空指针为什么危险,而且往往很难排查?
小结
知识点
- 指针/引用保存数据的位置而非内容
- 通过指针可在别处访问同一块数据
- 指针让数据不必连续存放
- 空指针、悬空指针、越界指针都很危险
参考资料
- Wikipedia(zh):指针 (计算机科学):保存内存地址的变量
- Wikipedia(zh):悬空指针:指向已释放内存的指针
思考题答案(仅供参考)
思考题 1
保存的是数据的位置(地址)。这样的好处是:数据本身可以放在任意地方,程序只要拿到地址就能在别处访问它;数据也不必连续存放,插入删除可以通过改指针完成,而不必整体挪动。
思考题 2
因为它指向的内存可能已经被释放或改作他用,再去访问就会读到垃圾或被别人改过的数据,导致难以预料的行为。更麻烦的是,它可能平时不出错、只在特定时机暴露,很难稳定复现,因此排查困难。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪