09 · 指针 ★核心中的核心

2026.09.27 · 🔴🔴 最难(Java 完全没有)

一句话理解:指针就是存「地址」的变量。 普通变量存数据,指针存数据在哪儿。

两个符号

int a = 10;
int *p = &a;      // & 取地址:把 a 的地址存进 p
printf("%d\n", *p);   // * 解引用:通过 p 拿到 a 的值 → 10
*p = 20;              // 通过指针修改 a → a 变成 20
  • &:取地址运算符(&a = a 的地址)
  • *:解引用运算符(*p = p 指向的那个值)
  • 声明时的 int *p 里的 * 只是「表示 p 是指针」,别和解引用混淆。

内存图(务必脑中有图)

内存地址      变量
0x7ffd1000    a = 10      ← &a = 0x7ffd1000
0x7ffd1008    p = 0x7ffd1000   ← p 存的是 a 的地址
              *p 顺着 p 里的地址找到 a

指针与数组的关系(必背)

int a[5] = {1,2,3,4,5};
int *p = a;          // 数组名 = 首元素地址
p + 1;               // 不是 +1 字节,而是 +1 个 int(步长 = sizeof(int) = 4)
*(a + i) == a[i];    // 完全等价

指针运算是按「类型大小」走的,int* 加 1 跳 4 字节,char* 加 1 跳 1 字节。

常见形态

写法 含义
int *p 指向 int 的指针
int **p 二级指针(指向指针的指针)
void *p 无类型指针,可指向任意类型(malloc 返回这个)
NULL 空指针,解引用会崩,用前必须判空
野指针 未初始化的指针,指向随机地址 → 解引用必崩

指针作为函数参数(最重要的用法)

void modify(int *x) { *x = 100; }   // 想让函数改外部变量,就传指针
int v = 1;
modify(&v);   // v 变成 100

安全铁律

  1. 指针必须初始化(int *p = NULL;),否则是野指针。
  2. 解引用前判空:if (p != NULL) { ... }。
  3. free 之后立刻把指针置 NULL(防悬空指针,见 19-内存管理)。
  4. 不要返回局部变量的地址(函数结束后栈内存失效)。

⚡ Java 对比

Java 没有指针,只有「对象引用」——引用是安全的、受 GC 管理的;C 的指针是裸露的地址,能指哪就指哪,也能指到不该指的地方然后崩溃。能力与风险同时放大。

关联

08-数组 · 10-函数指针与回调 · 11-字符串 · 19-内存管理 · 12-结构体