二级指针
2026/7/2大约 3 分钟
二级指针
一、什么是二级指针?
二级指针本质上还是一个变量,只不过:
一级指针存的是某个变量的地址,二级指针存的是一级指针变量本身的地址。
例如:
int a = 10;
int *p = &a; // p 保存 a 的地址
int **pp = &p; // pp 保存 p 的地址可以画成:
a
┌──────┐
│ 10 │
└──────┘
▲
│
│ p
┌──────┐
│ &a │
└──────┘
▲
│
│ pp
┌──────┐
│ &p │
└──────┘因此:
*p得到的是:
a 的值而:
*pp得到的是:
p继续:
**pp最终得到:
a 的值所以可以简单理解为:
*p
↓
找到 p 指向的对象
*pp
↓
找到 pp 指向的一级指针 p
**pp
↓
继续找到 p 指向的对象二、为什么函数修改指针需要二级指针?
看这个例子:
void alloc(int *p)
{
p = malloc(sizeof(int));
}
int *p = NULL;
alloc(p);函数中的:
p = malloc(...);修改的只是函数参数 p 的副本。
函数返回后,外面的:
p仍然是 NULL。
如果希望函数直接修改外面的指针,就要把:
外部一级指针
p的地址传进去。
void alloc(int **pp)
{
*pp = malloc(sizeof(int));
}
int *p = NULL;
alloc(&p);过程:
外部:
p = NULL
│
│ &p
▼
pp
│
│ *pp
▼
p函数执行:
*pp = malloc(...);实际上修改的就是外部的:
p所以:
一级指针
↓
保存对象地址
二级指针
↓
保存一级指针变量的地址
↓
因此可以间接修改一级指针本身三、常见使用场景
1. 函数修改指针本身
void alloc(int **pp)
{
*pp = malloc(sizeof(int));
}典型场景:
malloc- 动态数组
- 函数创建对象并返回指针
- 驱动中初始化并修改句柄
2. 链表操作
例如删除链表头节点:
void delete_head(Node **head)
{
Node *tmp = *head;
*head = tmp->next;
free(tmp);
}因为删除头节点后,需要修改:
head这个一级指针本身,所以传:
Node **head3. 指针数组
例如:
char *argv[];本质上是:
argv
↓
多个 char *
↓
每个 char * 指向一个字符串函数参数中常见:
char **argv但注意:
char \**不等于“二维数组”。
它可以用于表示指针数组,例如命令行参数;而真正的二维数组:
char arr[3][20];和 char ** 的内存布局并不相同,不能简单互换。
四、核心理解
不要死记:
“一级指针操作变量,二级指针操作指针。”
更准确的理解是:
指针本身也是变量
↓
一级指针:保存普通变量的地址
↓
二级指针:保存一级指针变量的地址
↓
三级指针:保存二级指针变量的地址因此:
*p
↓
访问 p 指向的对象
*pp
↓
访问 pp 指向的一级指针
**pp
↓
访问一级指针最终指向的对象一句话记忆:
二级指针没有什么特殊的,本质就是“指向一级指针的指针”;它最重要的用途之一,就是让函数能够修改调用者手里的一级指针。

