指针, C语言的精髓
莫队先咕几天, 容我先讲完树剖 (因为后面树上的东西好多都要用树剖求 LCA).
什么是指针
保存变量地址的变量叫做指针.
这是大概的定义, 但是Defad认为这个定义不太好理解, 所以我们先不看.
我们的电脑里都有随机存储器 RAM 也就是内存条, 所有变量和数组都在这里, 这个应该大家都知道.
那么我们把内存条看成数组, 头地址显然是 0
.
这也解释了为什么不能用
NULL
, 因为一般不用数组第 \(0\) 个.
那么现在我们有一个整数变量 x
.
cpp
int x;
这个 x
在内存条哪里呢?
&
可以获取变量在内存条的下标, 所以 x
的下标是 &x
.
那么我们怎么访问内存条的第 &x
项呢?
刚才我们说, 内存条头地址是 0
, 根据数组访问的方法, 可以想到用 0[&x]
访问变量 x
.
我并不是在瞎写, 我只是觉得这样比较好理解, 真正用的时候写这个的应该腰斩.
那么我们现在用一个变量存储这个 x
的下标.
cpp
int *p = &x;
int*
类型表示保存 int
类型变量在内存条里的下标的变量.
所以我们可以用 p
代替 &x
就是 0[p]
.
但是我们现在有一个语法糖.
*p
就是表示 0[p]
, 因为数组访问的本质是指针移动, 比如 a[3]
实际上是 *(a + (3))
.
所以我们可以用 *p
来访问在内存条里 p
保存的下标.
同理 *&x
也一样, 但是你变量都在这了就不需要取下标再访问下标了.
指针能干什么
我们有一个栈存储我们调用的函数和函数内部开的变量, 叫系统栈.
但是这样带来的问题就是我们给函数传入的变量是把值传进去了, 函数并不知道是要对值更改还是对变量本省更改.
现在有了指针就好办多了.
比如写一个交换函数.
cpp
void exchange(int *x, int *y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
就可以传入地址后在内部做交换.
刚才如果我没提到什么是地址, 那就是在内存条的下标了.
还有, 我们可以申请一块内存, 使用完再释放掉.
cpp
// 申请, 这里要强转成目标类型
int *a = (int*)malloc(sizeof(int) * 100);
// 这里做点什么
free(a); // 释放
申请来的可以当数组用, 也可以当变量用, 需要指针访问 (类似 *p
).
比如平衡树的一个结点, 插入时申请一个, 删除时释放掉.
这样其实相当于一个垃圾回收机制.
垃圾回收机制是说比如平衡树删除一个结点时下一个新建结点直接用刚删掉的结点拿过来.