2026/1/20 1:42:38
网站建设
项目流程
网站管理系统图片,淘宝代运营去哪里找,可以做免费推广的网站吗,培训好吗网站建设#x1f3e0;个人主页#xff1a;黎雁 #x1f3ac;作者简介#xff1a;C/C/JAVA后端开发学习者 ❄️个人专栏#xff1a;C语言、数据结构#xff08;C语言#xff09;、EasyX、游戏、规划 ✨ 从来绝巘须孤往#xff0c;万里同尘即玉京 文章目录 前景回顾#xff1a;前…个人主页黎雁作者简介C/C/JAVA后端开发学习者❄️个人专栏C语言、数据结构C语言、EasyX、游戏、规划✨ 从来绝巘须孤往万里同尘即玉京文章目录前景回顾前两篇指针核心速记 一、数组名的本质首元素地址的代名词 1. 数组名 首元素地址2. 数组名的两个例外情况 二、用指针访问数组灵活操作的新姿势 ✨1. 指针访问数组的多种方式2. 核心原理下标引用符[]的本质三、一维数组传参的本质传递的是地址 1. 代码演示数组传参的陷阱2. 结论四、冒泡排序指针思想的经典实战 1. 冒泡排序的核心逻辑2. 完整代码实现五、二级指针指向指针的指针 1. 二级指针的定义与理解2. 二级指针的解引用六、指针数组存放指针的数组 1. 指针数组的定义2. 指针数组的使用七、指针数组模拟二维数组巧妙的伪装 1. 模拟原理2. 核心等价关系写在最后 继前两篇指针基础与进阶内容后本篇聚焦指针与数组的核心关联同时讲解二级指针、指针数组的用法带你打通指针与数组的任督二脉彻底搞懂这些易混淆的知识点前景回顾前两篇指针核心速记 指针第一讲从内存到运算吃透指针核心逻辑指针第二讲const 修饰、野指针规避与传址调用想要学好本篇内容先巩固前两篇的关键要点指针基础地址就是指针指针变量用于存储地址通过取地址、*解引用操作变量。const与指针const在*左右位置不同限制的对象不同可保护数据不被意外修改。野指针规避指针必须初始化、避免越界、不用时置NULL、不返回局部变量地址。传址调用通过传递地址函数可直接修改主调函数中的变量是指针的核心实用场景。一、数组名的本质首元素地址的代名词 在C语言中数组名和指针有着密不可分的关系核心结论先记住数组名默认是数组首元素的地址。1. 数组名 首元素地址看代码验证#includestdio.hintmain(){intarr[10]{0};printf(arr %p\n,arr);printf(arr[0] %p\n,arr[0]);printf(arr %p\n,arr);return0;}运行结果中三个地址完全相同这说明arr和arr[0]都指向数组第一个元素的地址arr虽然地址值相同但含义却不一样。2. 数组名的两个例外情况 数组名并非在所有场景下都代表首元素地址有两个特殊场景数组名表示整个数组例外1sizeof(数组名)—— 计算的是整个数组的字节大小例外2数组名—— 取出的是整个数组的地址我们用代码看区别#includestdio.hintmain(){intarr[10]{0};printf(arr %p\n,arr);printf(arr1 %p\n,arr1);// 跳过1个int偏移4字节printf(arr[0] %p\n,arr[0]);printf(arr[0]1 %p\n,arr[0]1);// 跳过1个int偏移4字节printf(arr %p\n,arr);printf(arr1 %p\n,arr1);// 跳过整个数组偏移40字节return0;}关键区别arr1和arr[0]1指针类型是int*1跳过1个int4字节。arr1arr的类型是int (*)[10]指向包含10个int的数组的指针1跳过整个数组10×440字节。二、用指针访问数组灵活操作的新姿势 ✨既然数组名是首元素地址那我们就可以用指针来遍历和操作数组多种写法效果完全一致。1. 指针访问数组的多种方式#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};intszsizeof(arr)/sizeof(arr[0]);int*parr;// p指向数组首元素inti0;for(i0;isz;i){// 以下四种写法等价printf(%d ,arr[i]);// 下标法最常用printf(%d ,*(arri));// 数组名偏移量printf(%d ,p[i]);// 指针变量下标法printf(%d ,*(pi));// 指针变量偏移量// 还有一种特殊写法不推荐// printf(%d , i[arr]); // 等价于*(iarr) *(arri)}return0;}2. 核心原理下标引用符[]的本质C语言规定arr[i]等价于*(arri)这是下标引用符的本质。arr是首元素地址i表示跳过i个元素的地址。*解引用这个地址就得到了第i个元素的值。三、一维数组传参的本质传递的是地址 很多人会疑惑为什么数组传参后用sizeof计算的大小和原数组不一样答案很简单一维数组传参本质传递的是数组首元素的地址。1. 代码演示数组传参的陷阱#includestdio.hvoidtest(intarr[])// 这里的arr本质是int*指针不是数组{intsz2sizeof(arr)/sizeof(arr[0]);printf(sz2 %d\n,sz2);}intmain(){intarr[10]{0};intsz1sizeof(arr)/sizeof(arr[0]);printf(sz1 %d\n,sz1);// 输出10计算的是整个数组元素个数test(arr);return0;}运行结果32位系统sz110sz21指针大小4字节4/4164位系统sz110sz22指针大小8字节8/422. 结论函数形参写int arr[]和写int* arr是完全等价的形参接收的是一个指针变量不是整个数组。所以数组传参时必须额外传递数组的元素个数否则函数内部无法正确获取数组长度。四、冒泡排序指针思想的经典实战 冒泡排序是入门级排序算法核心思想是两两相邻元素比较逆序则交换每一趟排序都会让最大的元素浮到末尾。1. 冒泡排序的核心逻辑数组有sz个元素需要进行sz-1趟排序最后一个元素无需再比较。第i趟排序时只需要比较前sz-1-i个元素后面i个元素已经有序。2. 完整代码实现#includestdio.h// 冒泡排序函数voidbubble_sort(intarr[],intsz){inti0;// 控制排序趟数for(i0;isz-1;i){intj0;// 控制每一趟的比较次数for(j0;jsz-1-i;j){if(arr[j]arr[j1]){// 交换两个元素inttmparr[j];arr[j]arr[j1];arr[j1]tmp;}}}}// 打印数组函数voidprint_arr(intarr[],intsz){intj0;for(j0;jsz;j){printf(%d ,arr[j]);}printf(\n);}intmain(){intarr[]{9,8,7,6,5,4,3,2,1,0};intszsizeof(arr)/sizeof(arr[0]);printf(排序前);print_arr(arr,sz);bubble_sort(arr,sz);printf(排序后);print_arr(arr,sz);return0;}优化技巧如果某一趟排序没有发生任何交换说明数组已经有序可以提前结束排序提升效率。五、二级指针指向指针的指针 当一个指针变量的地址被另一个指针存储时这个指针就是二级指针它的作用是操作一级指针变量本身。1. 二级指针的定义与理解#includestdio.hintmain(){inta10;int*paa;// pa是一级指针存储a的地址int**ppapa;// ppa是二级指针存储pa的地址return0;}a是整型变量a是a的地址类型是int*。pa是一级指针变量pa是pa的地址类型是int**。2. 二级指针的解引用通过二级指针可以间接访问目标变量的值需要两次解引用#includestdio.hintmain(){inta10;int*paa;int**ppapa;printf(%d\n,a);// 直接访问a输出10printf(%d\n,*pa);// 一级解引用输出10printf(%d\n,**ppa);// 二级解引用输出10return0;}六、指针数组存放指针的数组 指针数组本质是数组只是数组中的每个元素都是指针类型的变量。1. 指针数组的定义格式类型* 数组名[元素个数];例如int* parr[4];数组parr有4个元素每个元素的类型是int*整型指针。对比普通数组int arr[4];的元素是int类型char arr[4];的元素是char类型。2. 指针数组的使用#includestdio.hintmain(){inta10;intb20;intc30;intd40;// 指针数组存储四个整型变量的地址int*parr[4]{a,b,c,d};inti0;for(i0;i4;i){printf(%d ,*(parr[i]));// 解引用每个元素输出10 20 30 40}return0;}七、指针数组模拟二维数组巧妙的伪装 二维数组在内存中是连续存储的而指针数组可以通过存储多个一维数组的首地址来模拟二维数组的效果。1. 模拟原理定义三个一维数组再用一个指针数组存储它们的首地址通过两层循环访问#includestdio.hintmain(){intarr1[5]{1,2,3,4,5};intarr2[5]{2,3,4,5,6};intarr3[5]{3,4,5,6,7};// 指针数组存储三个一维数组的首地址int*parr[3]{arr1,arr2,arr3};inti0;for(i0;i3;i){intj0;for(j0;j5;j){// parr[i][j] 等价于 *(*(parri)j)printf(%d ,parr[i][j]);}printf(\n);}return0;}2. 核心等价关系parr[i][j]*(*(parri)j)parri找到指针数组的第i个元素的地址。*(parri)解引用得到第i个一维数组的首地址。*(parri)j找到第i个一维数组的第j个元素的地址。*(*(parri)j)解引用得到目标元素的值。 注意指针数组模拟的二维数组各一维数组在内存中不一定连续而真正的二维数组内存是连续的。写在最后 本篇的核心是打通指针与数组的关联记住这几个关键结论数组名默认是首元素地址仅在sizeof(数组名)和数组名时代表整个数组。一维数组传参本质传地址函数形参是指针变量。二级指针用于操作一级指针变量指针数组是存放指针的数组。指针数组可以模拟二维数组但内存布局和真正的二维数组有区别。指针的学习到这里已经覆盖了大部分核心知识点后续可以结合字符串、函数指针等内容进一步深化。多敲代码、多调试观察内存地址的变化才能真正掌握指针的精髓