指针基础中的进阶用法:
int **p
是一个指向指针的指针,也称为“二级指针”或“指针的指针”。我们来系统地讲清楚这个用法。
int **p
是什么?
int **p;
含义:p 是一个指针,它指向另一个指向 int 类型的指针。
换句话说:
int
是数据int *
是指向整数的指针int **
是指向“指向整数的指针”的指针
图解(模拟内存结构)
int a = 10;
int *p1 = &a;
int **p2 = &p1;
结构图像这样
a = 10
&p1 = 地址1
p1 = &a ---> 指向 a 的地址
&p2 = 地址2
p2 = &p1 ---> 指向 p1 的地址
*p2 = p1 ---> 解引用一次,得到 p1
**p2 = *p1 ---> 解引用两次,得到 a
所以:
*p2 == 10
p2 == p1
p2 == &p1
示例代码演示
int main() {
int a = 42;
int *p1 = &a;
int **p2 = &p1;
printf("a = %d\\n", a);
printf("*p1 = %d\\n", *p1);
printf("**p2 = %d\\n", **p2);
return 0;
}
输出:
a = 42
*p1 = 42
**p2 = 42
二级指针常见应用场景
应用场景 | 举例 |
---|---|
修改指针的值(比如函数里改变传入指针) | void modify(int **pp) |
传递二维数组(模拟 int[][] ) | int **matrix |
命令行参数 main(int argc, char **argv) | 字符串指针数组 |
动态创建字符串数组或 int 数组的数组 | 动态内存管理时用 |
示例:通过函数修改指针值
void set_ptr(int **pp) {
static int val = 999;
*pp = &val;
}
int main() {
int *p = NULL;
set_ptr(&p);
printf("*p = %d\\n", *p); // 输出 999
}
总结一张表
声明 | 含义 | 指向 |
---|---|---|
int a | 普通整型 | 存储一个整数 |
int *p | 指针变量 | 指向一个整数 |
int **pp | 二级指针 | 指向一个“指向整数”的指针 |
二阶指针和二维数组
二维数组(2D Array)
int arr[3][4]; // 一个3行4列的二维整型数组
int **p; // 一个指向 int 指针的指针
可以用来动态创建二维结构,但内存是分散的。
内存结构
二维数组:
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
在内存中是连续的:
1 2 3 4 5 6
(按行优先顺序排列)
二阶指针模拟二维数组:
int **p = malloc(rows * sizeof(int *));
for (int i = 0; i < rows; ++i)
p[i] = malloc(cols * sizeof(int));
每一行是单独分配的,每一行的地址可能不连续。
使用方式比较
操作 | 二维数组 | 二阶指针 |
---|---|---|
声明方式 | int arr[3][4] | int **p |
内存分配 | 编译时固定 | 运行时动态分配 |
内存结构 | 连续 | 分散(非连续) |
访问方式 | arr[i][j] | p[i][j] |
可变大小 | 否(除非用变长数组) | 是,适合动态数组 |
使用场景
二维数组:适合大小固定、效率优先、栈上分配的场景。
二阶指针:适合动态创建二维结构(如动态输入表格),但管理起来更复杂(需手动释放每一行内存)。
注意事项
不要把
int arr[3][4]
当成int **
来用,它们底层结构不一样,不能强转。如果需要传递二维数组给函数,声明应为:
void func(int arr[][4]) // 或 func(int (*arr)[4])
对于二阶指针则:
void func(int **p)