C语言指针的三个进阶形态

📅 2026/8/25 3:55:14
C语言指针的三个进阶形态
C语言指针的三种进阶形态概念写法本质类比指向指针的指针二层指针int **pp一个变量里面存的是另一个指针变量的地址“门牌号本子的存放抽屉号”指针数组int *arr[10]一个数组里面每个元素都是指针“一排门牌号本子”函数指针void (*f)(int)一个变量里面存的是函数的入口地址“记录修鞋铺位置的纸条”1.int **pp指向指针的指针你有一张纸条p上面写着101室a。你又拿了一张纸条pp上面写着放p的那个抽屉的编号。所以*pp取出的是p101室**pp取出的是a的值。int a 5; int *p a; // p存的是a的地址 int **pp p; // pp存的是p的地址 printf(%p\n, *pp);//输出p的地址 printf(%d\n, **pp); // 输出5嵌入式里什么时候用函数要修改外部指针本身如分配内存后改变指针指向。操作二维动态数组行指针数组。//函数要修改外部指针指针示例: //让函数 alloc_memory 把 malloc 申请到的内存地址“带出来”赋给外部的指针 p void alloc_memory(int **pp) { *pp malloc(100); // 改变外部指针 p 本身 } int *p NULL; alloc_memory(p); // 现在 p 指向了堆上 100 字节 //操作二位动态数组示例 //用二层指针 matrix 创建一个“动态二维数组”即行数和列数都在运行时由 rows 和 cols 决定 //而不是编译时固定。 int **matrix malloc(rows * sizeof(int*)); for (int i 0; i rows; i) { matrix[i] malloc(cols * sizeof(int)); }2.int *arr[3]指针数组指针数组的目的用一个数组统一管理多个地址方便批量操作这些地址指向的数据。这是一个数组里面有3个元素每个元素都是int*类型指向int的指针。int a1, b2, c3; int *arr[3] {a, b, c}; printf(%d, *arr[0]); // 输出1int main(int argc, char *argv[])argc参数个数。argv这是一个指针数组每个元素指向一个字符串命令行输入的内容。例如输入./a.out hello world则argv[0]→./a.outargv[1]→helloargv[2]→world这就是典型的指针数组用法操作系统把命令行字符串的起始地址全部放进这个数组里传给你。使用场景场景例子本质字符串数组char *argv[] {ls, -l, NULL};每个元素指向一个字符串函数跳转表void (*cmd[])(void) {func1, func2, func3};用下标直接调用不同函数多对象管理int *bufs[4];每个指向不同传感器数据缓冲区用循环统一处理所有对象与二层指针对比写法本质内存典型场景int *arr[10]数组里面存了10个地址40字节10个指针固定数量指针编译时确定int **pp指针指向一个指针变量4或8字节1个指针需要动态分配行数二维数组或函数内修改外部指针指针数组 多个指针的“收纳盒”让你能用循环一次性处理所有地址。3.void (*f)(int)函数指针定义一个变量f它指向一个“参数为int、返回值为void”的函数。void print_num(int n) { printf(%d, n); } void (*f)(int) print_num; // f指向print_num函数 f(5); // 调用输出5嵌入式里什么时候用中断向量表每个中断号对应一个函数地址。回调函数比如定时器溢出时调用你指定的函数。状态机跳转表根据当前状态自动执行对应函数。4.总结写法类型占内存取内容后得到int *p;指向int的指针4字节地址int值int **pp;指向“指向int的指针”的指针4字节int *值int *arr[10];有10个元素的数组每个元素是int*40字节4×10int值经过两次解引用void (*f)(int);指向函数的指针4字节地址函数入口地址调用即执行题目请用C语言写一个程序实现以下功能定义三个整数a10, b20, c30。定义一个指针数组arr让它的三个元素分别指向a, b, c。定义一个指向指针的指针pp让它指向arr的第二个元素即指向b的那个元素。定义一个函数指针f指向一个函数print_value该函数接收一个int*指针并打印它指向的值。用pp取出b的值并用f打印它。#include stdio.h void print_value(int *p) { printf(value %d\n, *p); } int main() { int a 10, b 20, c 30; // 1. 指针数组每个元素指向一个int int *arr[3] {a, b, c}; // 2. 指向指针的指针指向arr的第2个元素即b int **pp arr[1]; // 或者 arr[1] 等价于 arr 1 // 3. 函数指针 void (*f)(int*) print_value; // 4. 通过pp取出b的值用f打印 f(*pp); // *pp arr[1] b所以 f(b) 打印20 return 0; }补充三层指针1、“一层指针”是什么int a 5; int *p a;a是一个房间里面住了5门牌号是0x1000。p是一个“门牌号本子”上面写着0x1000。一层指针 存地址的变量。2、二层指针存“门牌号本子”的“抽屉号”int **pp p;pp是一个“抽屉”里面放的不是门牌号而是“存放门牌号本子的抽屉号”。也就是说pp指向的是p这个变量本身。画出来就是pp (0x2000) → p (0x1000) → a (5)pp的值是0x2000p 的地址*pp取出的是p的值即0x1000**pp取出的是a的值即53、什么时候必须用二层指针核心场景场景1函数里要改变“指针变量本身”如果你写void set_null(int *p) { p NULL; // 改的是 p 的副本外部的 p 不受影响 }传进去的p是复制品改它没用。要让函数把外面的指针改成NULL必须传指针的地址void set_null(int **pp) { *pp NULL; // 直接改外部的指针变量 } int *p a; set_null(p); // 传的是 p 的地址 // 现在 p 变成 NULL 了本质你要改的是“本子上的内容”但本子本身是别人的所以你只能把“本子的存放位置”告诉函数让它去那里改。场景2动态分配二维数组int **matrix; matrix (int **)malloc(rows * sizeof(int *)); for (int i 0; i rows; i) { matrix[i] (int *)malloc(cols * sizeof(int)); }matrix是一层指针指向一个“指针数组”的首地址。每个matrix[i]又是一个指针指向一行数据。访问matrix[1][2]时编译器先找第1行指针再找第2列数据。场景3操作系统命令行参数int main(int argc, char **argv)argv是一个二层指针等价于char *argv[]。它指向一个指针数组数组里每个元素指向一个命令行字符串。操作系统通过二层指针把多个字符串的地址一次性传给你。4、三层指针有没有有但极少见。比如int ***ppp;ppp指向pppp指向pp指向a。只有在三维动态数组或者非常深的嵌套修改场景才会用到普通工程基本碰不到。5、总结一层指针存地址二层指针存“存地址的变量”的地址。需要修改指针本身时如函数内改外部的指针、动态分配二维数组就用二层指针。6、题目问为什么swap_ptr的参数必须是int **而不能是int *void swap_ptr(int **pp1, int **pp2) { int *temp *pp1; *pp1 *pp2; *pp2 temp; } int a1, b2; int *p1 a, *p2 b; swap_ptr(p1, p2); // 现在 p1 指向 bp2 指向 a答因为要交换的是p1和p2这两个指针变量本身而不是它们指向的值。传int *只能交换复制品传int **才能直接改原件。