了解指针3

📅 2026/7/13 15:19:54
了解指针3
1. 数组名的理解三组关键地址对比核心代码对比arr[0]、arr、arr三者intarr[10]{1,2,3,4,5,6,7,8,9,10};printf(arr[0] %p\n,arr[0]);// 首元素地址printf(arr[0]1 %p\n,arr[0]1);// 4 字节printf(arr %p\n,arr);// 同首元素地址printf(arr1 %p\n,arr1);// 4 字节printf(arr %p\n,arr);// 整个数组的地址printf(arr1 %p\n,arr1);// 40 字节跳过整个数组输出示例x86arr[0] 0077F820 arr[0]1 0077F824 arr 0077F820 arr1 0077F824 arr 0077F820 arr1 0077F848表达式含义1 后的偏移arr[0]首元素地址4 字节x86arr数组名等同于首元素地址4 字节arr整个数组的地址40 字节整个数组大小关键结论arr[0]、arr、arr三者的数值相同但类型不同——arr和arr[0]是int*arr是int(*)[10]所以arr1会跳过整个数组。2. 使用指针访问数组intarr[10]{0};inti0;intszsizeof(arr)/sizeof(arr[0]);// 方式一指针写法int*parr;for(i0;isz;i){scanf(%d,pi);// 等价于 scanf(%d, arr i);}for(i0;isz;i){printf(%d ,*(pi));// 等价于 printf(%d , p[i]);}// 方式二下标写法for(i0;isz;i){printf(%d ,p[i]);// p[i] 即 *(pi)}数组名arr和指针p在访问方式上可以互换*(p i)p[i]arr[i]*(arr i)3. 一维数组作为函数参数重要陷阱⚠️数组传参时会退化为指针#includestdio.h// x86 环境voidtest(intarr[])// 等价于 void test(int* arr){intsz2sizeof(arr)/sizeof(arr[0]);// ❌ arr 已退化为指针不代表整个数组的地址printf(sz2 %d\n,sz2);// 输出 2x64: 8/4}intmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};intsz1sizeof(arr)/sizeof(arr[0]);// ✅ 输出 10printf(sz1 %d\n,sz1);test(arr);return0;}验证函数内部sizeof(arr)的值为 4x86或 8x64即指针变量的大小。关键结论函数内部无法通过sizeof(arr)/sizeof(arr[0])获取数组长度必须在调用前计算好sz并作为参数传入。4. 冒泡排序综合应用版本1标准冒泡排序效率较低voidbubble_sort(intarr[],intsz)// 形参数组实为指针{inti0;for(i0;isz-1;i)//i指的是元素的下标{intj0;for(j0;jsz-i-1;j)//比较相邻值的次数{if(arr[j]arr[j1])//当发现j的值比j1的值大就交换{inttmparr[j];//创建tmp变量交换arr[j]arr[j1];arr[j1]tmp;}}}}intmain(){intarr[]{3,1,7,5,8,9,0,2,4,6};intszsizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz);inti0;for(i0;isz;i){printf(%d ,arr[i]);}return0;}为什么是 9 轮因为数组有 10 个元素sz 10每轮固定一个最大值到最后。固定好前 9 个后剩下的 1 个自然就在正确位置了不需要再比第 10 轮。第 1 轮i0比较 9 次比较相邻元素大的往后移结果最大值 9 被“冒泡”到最后 → [3, 1, 7, 5, 8, 0, 2, 4, 6, 9]✅ 最后 1 个位置下标 9已固定下一轮不再管它第 2 轮i1只比较前 9 个比较8 次j 从 0 到 7只比较 arr[0] 到 arr[8]结果次大值 8 被冒泡到倒数第 2 位 → [1, 3, 5, 7, 0, 2, 4, 6, 8, 9]✅ 倒数第 2 个位置下标 8也固定了第一轮一定会把最大的数值排到最后后面的轮次以此类推每轮少比较 1 次因为末尾已固定的元素越来越多。版本2优化版带 flag 提前终止voidbubble_sort(intarr[],intsz)// 形参数组实为指针{inti0;for(i0;isz-1;i){intflag1;// 假设已经有序intj0;for(j0;jsz-i-1;j){if(arr[j]arr[j1]){flag0;// 发生交换说明未排好inttmparr[j];arr[j]arr[j1];arr[j1]tmp;}}if(flag1)// 当到这个里flag还1即已经有序{break;//跳出函数}}}intmain(){intarr[]{3,1,7,5,8,9,0,2,4,6};intszsizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz);inti0;for(i0;isz;i){printf(%d ,arr[i]);}return0;}5. 二级指针把指针a的地址存到一个指针b指针b指向的是指针a的地址#includestdio.hintmain(){inta10;int*paa;// 一级指针存 a 的地址int**ppapa;// 二级指针存 pa 的地址return0;}ppa (int**) pa (int*) a (int) ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 0x0012ff48 │ ────→ │ 0x0012ff40 │ ────→ │ 10 │ └──────────────┘ └──────────────┘ └──────────────┘ 地址:0x0012ff4c 地址:0x0012ff48 地址:0x0012ff40 解引用两次 解引用一次 **ppa 10 *pa 10 *ppa 0x0012ff40 pa 0x0012ff40 ppa 0x0012ff4c pa 0x0012ff4c*ppapa即*ppa得到pa中存的地址**ppa*paa。操作等价于效果*ppapa获取一级指针pa中存的地址**ppa*pa→a访问原始变量a*ppa bpa b让pa重新指向b**ppa 30a 30修改a的值6. 指针数组存放地址指针的数组int*parr[3]{arr1,arr2,arr3};// int * parr[3] 是存放 3 个 int* 的数组parr是一个数组每个元素是int*类型指针。parr[i]是数组中第i个int*指针parr[i][j]访问第i个数组的第j个元素。7. 指针数组的应用 — 模拟二维数组#includestdio.hintmain(){intarr1[]{1,2,3,4,5};intarr2[]{2,3,4,5,6};intarr3[]{3,4,5,6,7};int*parr[3]{arr1,arr2,arr3};// 指针数组inti0;intj0;for(i0;i3;i)//指向各数组首元素的地址{for(j0;j5;j)//指向各数组里的元素地址{printf(%d ,parr[i][j]);// 类似二维数组的访问}printf(\n);}return0;}输出1 2 3 4 5 2 3 4 5 6 3 4 5 6 7通过指针数组parr管理多个一维数组实现类似二维数组的访问方式。总结重点掌握数组名与arr的区别数值相同类型不同int*vsint(*)[10]数组传参退化为指针函数内sizeof(arr)得到的是指针大小必须额外传sz二级指针int**指向int*解引用一层得到指针解引用两层得到原变量指针数组int* arr[]存放指针的数组可用于模拟二维数组