
前言学完一级指针后很多同学会被二级指针、指针数组、数组指针、const 修饰指针、void 万能指针绕晕本文结合课堂学习笔记用通俗例子 完整代码拆解适合零基础 C 语言初学者收藏复习。前置知识掌握一级指针int *p、取地址、解引用*、数组基础一、二级指针指针的指针1. 核心概念一级指针int *p存放普通变量的地址 二级指针int **pp存放一级指针变量本身的地址可以理解为「指针的指针」CSDN博...。生活化类比普通变量int a 10家里存放的数据一级指针int *p a一张纸条写着家的地址二级指针int **pp p一个笔记本记录纸条存放的位置2. 定义、初始化与解引用规则语法数据类型 **二级指针名;完整示例代码c运行#include stdio.h int main(void) { int a 100; int *p a; // 一级指针p保存变量a的地址 int **pp p; // 二级指针pp保存一级指针p的地址 printf(变量a的值%d\n, a); printf(一级指针p a%p\n, p); printf(*p解一级指针取a的值%d\n, *p); printf(二级指针pp p%p\n, pp); printf(*pp解一次二级指针得到一级指针p的值%p\n, *pp); printf(**pp解两次二级指针最终拿到a的值%d\n, **pp); return 0; }解引用核心逻辑pp二级指针自身存一级指针地址*pp第一次解引用等价于一级指针p**pp第二次解引用等价于原始变量a3. 二级指针两大核心使用场景场景 1函数内部修改外部一级指针的指向C 语言函数传参是值传递直接传一级指针无法修改外部指针的指向必须传二级指针。 需求封装函数让外部一级指针指向新变量c运行#include stdio.h // 接收二级指针修改外部一级指针 void changePoint(int **pp) { int b 999; *pp b; // *pp等价于外部的一级指针p修改p的指向 } int main(void) { int a 100; int *p a; printf(修改前*p %d\n, *p); changePoint(p); // 传入一级指针p的地址二级指针 printf(修改后*p %d\n, *p); return 0; }场景 2接收指针数组作为函数参数指针数组的数组名隐式转换为二级指针下文会详细讲解。4. 多级指针拓展了解即可三级指针int ***ppp pp逻辑同理三层解引用***ppp日常开发最多用到二级指针三级及以上极少使用。二、指针数组 VS 数组指针新手最易混淆1. 指针数组本质是数组数组元素全为指针定义语法数据类型 *数组名[长度];[]优先级高于*先结合[]代表数组每个元素是对应类型指针。示例 1int 型指针数组c运行#include stdio.h int main(void) { int a 10, b 20, c 30; // 指针数组arr3个元素每个元素是int*指针 int *arr[3] {a, b, c}; int **p arr; // 数组名arr等价于arr[0]类型为int**二级指针 printf(arr[0] %p*arr[0] %d\n, arr[0], *arr[0]); printf(**(p1) %d\n, **(p1)); // p1指向第二个指针元素两次解引用取数值 return 0; }示例 2存储多组字符串最常用场景C 语言没有字符串类型用char*指针数组存放多个字符串节省内存c运行#include stdio.h int main(void) { // 指针数组每个char*指向字符串常量 char *month[] {January, February, March}; char **p month; printf(第二个月份%s\n, *(p1)); return 0; }2. 数组指针本质是指针专门指向一整行数组定义语法数据类型 (*指针名)[数组列长];必须加括号(*p)让*先和变量结合表示指针指针一次性指向完整数组。示例二维数组搭配数组指针c运行#include stdio.h int main(void) { // 2行3列二维数组 int arr[2][3] {1,2,3,4,5,6}; // 数组指针p指向包含3个int元素的一维数组 int (*p)[3] arr; // 多种等价访问方式全部输出arr[1][2] 6 printf(%d\n, arr[1][2]); printf(%d\n, *(*(p1)2)); printf(%d\n, *(p[1]2)); printf(%d\n, p[1][2]); return 0; }核心规律p arr数组指针指向二维数组第一行p1跳过一整行3 个 int偏移 12 字节*(p1)等价第二行数组名arr[1]指向本行第一个元素3. 两者对比速记表表格类型定义写法本质核心用途指针数组int *arr[5]数组元素是指针存储多个字符串、多个变量地址数组指针int (*p)[5]指针指向完整一维数组遍历、传参二维数组三、const 修饰四种指针必考知识点const表示只读口诀const 离谁近谁不能修改1.const int *p;等价int const *p;含义指向只读数据的指针权限指针p可以更换指向不能通过*p修改指向的值c运行int a 10, b 20; const int *p a; p b; // 合法修改指针指向 // *p 99; // 报错禁止修改指向内容2.int *const p;含义指针本身是常量常量指针权限指针p初始化后不能换指向可以通过*p修改值注意定义时必须初始化c运行int a 10; int *const p a; *p 99; // 合法修改内容 // p b; // 报错指针指向固定不可修改3.const int *const p;等价int const *const p;含义指针、指向内容全只读权限指针不能换指向也不能修改指向的数据c运行int a 10; const int *const p a; // p b; 报错 // *p 99; 报错四、void* 万能通用指针1. 特性void*可以接收任意类型变量的地址int、char、float、数组等不能直接*解引用编译器不知道指向数据占多少字节强制转换类型后才能解引用不能做指针算数运算/--无固定偏移步长完整示例c运行#include stdio.h int main(void) { int a 100; char ch A; float f 3.14f; void *p NULL; // 万能指针 p a; printf(int值%d\n, *(int*)p); // 强制转换int*再解引用 p ch; printf(char值%c\n, *(char*)p); p f; printf(float值%f\n, *(float*)p); return 0; }实际用途函数通用参数写一个函数兼容所有数据类型如内存拷贝memcpy底层就是 void*。五、补充易错点总结课堂笔记提炼所有指针变量在 64 位 Linux 下统一占 8 字节和前面数据类型无关类型只决定解引用读取字节数、指针偏移步长。野指针未初始化 / 指向已释放内存的指针禁止解引用定义指针默认赋值NULL。空指针NULL本质地址 0只读对NULL解引用程序直接崩溃。数组名两种场景不能当成指针sizeof(数组名)、数组名 数组名类型升级为数组指针。二级指针和指针数组配套char *str[]数组名是char**传参函数形参必须写char **p或char *p[]。六、课后练习配套学习巩固封装函数传入二级指针修改外部 char * 字符串指针的指向使用指针数组存储 12 个月份英文循环遍历全部打印定义二维数组int arr[3][4]用数组指针遍历打印所有元素分别写出四种 const 指针注释标注允许 / 禁止操作用 void * 指针实现通用打印函数可输出 int、char、float 数据。