C 语言 数组与指针深度解析 + 冒泡排序实战前言

发布时间:2026/6/11 9:25:01

C 语言 数组与指针深度解析 + 冒泡排序实战前言 前言在 C 语言中数组和指针密不可分数组名本质上会隐式转换为指针这也是初学阶段最容易混淆的知识点。本文从数组名含义、指针访问数组、数组传参本质入手最后结合指针知识实现冒泡排序完整梳理一维数组 指针核心考点。环境说明VS 编译器添加#define _CRT_SECURE_NO_WARNINGS 1消除安全警告。一、数组名到底是什么1. 基础结论普通场景下数组名 数组首元素的地址。代码验证c运行#define _CRT_SECURE_NO_WARNINGS 1 #includestdio.h int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; printf(arr[0] %p\n, arr[0]); printf(arr %p\n, arr); return 0; }运行结果会发现arr[0]和arr打印地址完全一致。 结论数组名arr等价于arr[0]都是首元素地址。2. 数组名的两个特殊例外重点数组名不是任何时候都代表首元素地址存在两种特例此时数组名代表整个数组sizeof(数组名)单独使用数组名作为sizeof参数计算整个数组的总字节大小。数组名取出整个数组的地址。3. 三种地址的本质区别我们先看地址打印c运行int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; printf(arr[0] %p\n, arr[0]); printf(arr %p\n, arr); printf(arr %p\n, arr); return 0; }三者地址数值看起来相同但类型、指针运算规则完全不同。指针运算对比核心考点指针1的规则向后跳过一个「自身类型」的大小c运行int main() { int arr[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); printf(arr %p\n, arr); printf(arr1 %p\n, arr 1); printf(arr %p\n, arr); printf(arr1 %p\n, arr 1); return 0; }arr[0]、arr类型为int*整型指针1向后偏移4 字节一个int大小。arr类型为int (*)[10]数组指针指向整个长度为 10 的 int 数组1向后偏移整个数组大小 10 * 4 40 字节。总结地址数值相同 ≠ 类型相同指针运算看指针类型不看地址数值。二、使用指针访问数组既然数组名是首元素指针那么完全可以用指针变量遍历、访问数组。1. 指针遍历数组写法 1解引用c运行int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; int sz sizeof(arr) / sizeof(arr[0]); int* p arr; // p 接收数组首元素地址 int i 0; for (i 0; i sz; i) { printf(%d , *(p i)); } return 0; }解析pi指向数组第i个元素*(pi)取出对应元素值。2. 下标访问p[i]语法C 语言中下标[]只是指针运算的语法糖a[b]等价于*(a b)因此arr[i]→*(arr i)p[i]→*(p i)代码演示c运行int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; int sz sizeof(arr) / sizeof(arr[0]); int* p arr; int i 0; for (i 0; i sz; i) { printf(%d , p[i]); } return 0; }拓展趣味写法 因为加法交换律ab ba所以arr[i]等价于i[arr]语法合法但不推荐使用。三、一维数组传参的本质高频易错点1. 核心结论一维数组传参本质传递的是数组首元素的地址指针。函数形参接收数组时编译器会隐式将数组形参转换成指针所以下面两种写法完全等价运行void test(int arr[]) // 本质还是指针 void test(int* arr) // 标准指针写法2. 为什么传参后不能用sizeof求数组长度c运行void test(int arr[]) { // arr 是指针变量不是数组 printf(sizeof(arr) %d\n, sizeof(arr)); } int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; // main 中 arr 代表整个数组 int sz1 sizeof(arr) / sizeof(arr[0]); printf(数组元素个数%d\n, sz1); test(arr); // 传递首元素地址 return 0; }运行结果分析main函数内sizeof(arr)计算整个数组大小可正常求出元素个数。test函数内arr是指针变量64 位环境下指针占 8 字节无法计算数组长度。重要规则数组传到函数内部后丢失数组长度信息所以一般需要额外传一个长度参数。四、结合指针实现冒泡排序1. 算法思路冒泡排序核心相邻两个元素两两比较前一个大于后一个则交换每一轮遍历都会把当前最大值冒泡到无序区末尾优化增加标记位如果某一轮没有发生任何交换说明数组已有序直接提前退出。2. 完整代码修正 BUG 版原代码存在逻辑错误if (x 0)跳出循环下面是修复后可直接运行的代码c运行#define _CRT_SECURE_NO_WARNINGS 1 #includestdio.h // 冒泡排序数组本质传指针第二个参数为数组长度 void bubble_sort(int* arr, int sz) { // 外层循环控制冒泡总趟数最多 sz-1 趟 int i 0; for (i 0; i sz - 1; i) { int j 0; int flag 1; // 标记默认本轮数组已有序 // 内层循环相邻元素比较-i 跳过后面已排好序的元素 for (j 0; j sz - 1 - i; j) { // 前 后交换 if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; flag 0; // 发生交换数组仍无序 } } // 本轮无交换 → 数组全局有序提前结束排序 if (flag 1) { break; } } } int main() { int arr[] { 2,4,7,1,0,8,13,22,52 }; // 主函数内计算数组长度 int sz sizeof(arr) / sizeof(arr[0]); // 数组传参传递首元素地址 bubble_sort(arr, sz); // 遍历打印排序后的数组 int i 0; for (i 0; i sz; i) { printf(%d , arr[i]); } return 0; }3. 代码解析函数形参int* arr接收数组首地址符合数组传参规则外层循环最多执行sz-1轮冒泡内层循环sz-1-i每一轮结束末尾i个元素已有序无需重复比较优化标记 flagflag 1假设数组有序发生交换则置flag 0一轮结束flag仍为 1 → 数组完全有序提前break。4. 运行结果plaintext0 1 2 4 7 8 13 22 52五、全文知识点总结数组名规则常规场景数组名 首元素地址int*类型特例sizeof(数组名)、数组名代表整个数组。指针运算指针1偏移大小由指针类型决定。下标本质arr[i] *(arri)[]是指针运算语法糖。数组传参本质传首元素地址形参等价为指针函数内无法用sizeof求数组长度。冒泡排序相邻比较交换 有序标记优化依托指针 / 下标访问数组完成排序。补充常见踩坑清单混淆赋值和判断相等排序标记判断极易出错误以为arr和arr完全一样忽略数组指针和整型指针的区别数组传参后仍想用sizeof计算数组长度。

相关新闻