
数组的认识大家好呀好久不见转眼假期已经到啦大一上半程的校园生活伴着期末复习备考的忙碌告一段落我也总算对大学的学习节奏慢慢熟悉了。这段时间跟着课程啃完了 C 语言指针的内容也刷了不少对应的练习题虽说能跟着做题目但总觉得学编程不能一直往前赶适时停下来回顾总结把基础打牢才更重要。思来想去就从数组这个 C 语言的核心基础开始重新梳理一遍之前的知识点再走一趟 C 语言的学习旅程一、C 语言数组的基础认知1.1什么是数组核心定义在 C 语言里数组是一组相同数据类型元素的连续存储集合核心作用是批量管理同类数据比如用一个数组存 10 个学生的成绩比定义 10 个单独变量更简洁。1.2核心特点类型严格统一数组里所有元素必须是同一种 C 语言基础类型如 int、char、float不能混存比如不能在 int 数组里放字符串。内存连续数组在内存中是 “挨着放” 的。索引从 0 开始访问数组元素靠 “索引下标”第一个元素是 arr [0]最后一个是 arr [长度 - 1]越界访问如 arr [10]会导致程序出错。1.3数组的使用价值解决什么问题批量处理同类型数据替代大量单独定义的变量让代码更简洁、易维护是处理表格、成绩、序列等批量数据的核心工具。二、一维数组核心重点C 语言数组基础2.1 一维数组的定义与初始化2.1.1标准定义格式type arr_name[常量值]type 是数组中存放数据的类型如 int、char、float 等也可以是自定义类型arr_name 是数组的名字[] 内是数组的大小代表元素个数。2.1.23 种常见初始化方式// 1. 完全初始化给所有元素赋值 int num1[3] {1, 2, 3}; // 数组num1[1, 2, 3] // 2. 部分初始化未赋值元素默认是0 int num2[3] {1}; // 数组num2[1, 0, 0] // 3. 省略长度编译器按初始化元素个数自动算长度此时长度是2 int num3[] {5, 6}; // 数组num3[5, 6]2.2 一维数组的访问与遍历2.2.1元素访问方式1.基础方式索引下标访问最常用C 语言数组通过 “索引下标” 定位元素索引从 0 开始对应数组第一个元素最大值为 “数组长度 - 1”。2.进阶方式指针偏移访问C 语言中数组名本质是 “指向数组第一个元素的常量指针”即数组名 数组 [0]。#includestdio.h int main() { int num1[3] { 1, 2, 3 }; printf(%d, num1[0]);//1 return 0; }2.2.22 种遍历方法for 循环 sizeof 通用求长sizeof 通用求长方法int len sizeof(数组名) / sizeof(数组名[0]);在操作 C 语言数组时用 sizeof 求长度是最实用的基础技巧能替代硬编码数字避免后续修改数组时漏改长度导致的越界问题一维、二维数组都能适配核心原理就是总字节数 ÷ 单个元素字节数结尾有补充。#define _CRT_SECURE_NO_WARNINGS #includestdio.h int main() { int num1[3] { 0 }; int sum sizeof(num1) / sizeof(num1[0]); printf(请输入元素的值); for(int i 0; i sum; i)//数组元素的输入 { scanf(%d, num1[i]); } printf(数组元素的打印\n); for (int i 0; i sum; i)//数组元素的打印 { printf(%d ,num1[i]); } return 0; }2.2.3 数组越界问题核心风险int arr[5] {1,2,3,4,5}; arr[5] 10; // 越界合法下标仅0-4 for(int i0; i5; i){} // 越界条件应写i52.3 一维数组的内存布局#define _CRT_SECURE_NO_WARNINGS #include stdio.h int main() { int num1[3] { 1,2,3 }; for (int i 0; i 3; i) { printf(%d , num1[i]); printf(arr[%d] %p\n, i, num1[i]); } return 0; }从输出可以看到arr[0] 000000A0F54FF828arr[1] 000000A0F54FF82Carr[2] 000000A0F54FF830计算一下相邻地址的差值0x000000A0F54FF82C - 0x000000A0F54FF828 40x000000A0F54FF830 - 0x000000A0F54FF82C 4我们总结一下内存存储特征数组元素在内存中连续不间断存储同类型元素占字节数一致地址随下标依次递增递增步长 单个元素字节数。数组名的本质数组名是数组首元素的地址常量不可修改如 arr 属于语法错误sizeof (数组名) 计算的是整个数组的总字节数。下标访问的底层逻辑数组下标访问 arr [i] 的本质是指针偏移解引用等价于 *(arr i)以首元素地址为基准向后偏移 i 个元素的字节数再取对应内存的值。三、二维数组一维数组的延伸多维数组基础3.1 二维数组的核心理解本质是 “一维数组的数组”二维数组可以看作是由多个相同长度的一维数组组成的数组C 语言中按 “行优先” 规则存储本质上还是一维数组的嵌套用于处理表格、矩阵等二维结构数据。3.2 二维数组的定义与初始化3.2.1标准定义格式type arr_name[常量值1] [常量值2]int num1[3][6]; double num2[3][6];解释3 表示数组有 3 行6 表示每一行有 6 个元素3.2.2 4 种灵活初始化方式按行完全初始化最清晰推荐把每一行的元素用 {} 包裹结构一目了然。int arr[2][3] {{1, 2, 3}, {4, 5, 6}};// 外层 {} 包含整个二维数组。// 内层每个 {} 对应一行的元素。按行部分初始化未初始化的元素会自动补 0。int arr[2][3] {{1}, {4, 5}};// 结果arr [0] {1, 0, 0}arr [1] {4, 5, 0}省略行长度初始化可以省略行数编译器会根据初始化的元素个数自动计算行数但列长度不能省略。int arr[][3] {1, 2, 3, 4, 5, 6};// 编译器自动推断行数为 2等价于 int arr [2][3]紧凑初始化不分行包裹按 “行优先” 顺序依次填充元素可读性稍差。int arr[2][3] {1, 2, 3, 4, 5, 6};// 效果与方式 1 完全一致3.2.3关键注意事项列长度不可省略无论是否省略行数二维数组的列长度必须显式指定否则编译器无法确定每行的元素个数。默认补 0 规则如果初始化元素不足未赋值的元素会被默认初始化为 0。全局 vs 局部数组全局 / 静态二维数组默认所有元素为 0局部二维数组未初始化时元素为随机值。3.3 二维数组的访问与遍历3.3.1元素访问方式索引下标访问这是最常用的访问方式C 语言数组通过 “索引下标” 定位元素行、列索引均从 0 开始最大值为 “对应维度长度 - 1”。进阶方式指针偏移访问在 C 语言中二维数组名本质是 “指向第一行一维数组的常量指针”即 数组名 数组 [0]。3.3.2双层 for 循环遍历#define _CRT_SECURE_NO_WARNINGS #include stdio.h int main() { int num2[2][3] {0}; for (int i 0; i 2; i)//⼆维数组的输⼊ { for (int j 0; j 3; j) { scanf(%d, num2[i][j]); } } printf(二维数组的输出如下\n); for (int i 0; i 2; i)//⼆维数组的输出 { for (int j 0; j 3; j) { printf(%d , num2[i][j]); } printf(\n); } return 0; }3.4 二维数组的内存布局我们依旧用实例说明#define _CRT_SECURE_NO_WARNINGS #include stdio.h int main() { int num2[2][3] { 0 }; int num 1; for (int i 0; i 2; i)//遍历赋值并打印地址 { for (int j 0; j 3; j) { printf(第%d个元素的地址为%p \n,num, num2[i][j]); num; } } return 0; }我们来计算每两个相邻地址的差值第 2 个元素地址 - 第 1 个元素地址0x000000B56E8FF95C - 0x000000B56E8FF958 4第 3 个元素地址 - 第 2 个元素地址0x000000B56E8FF960 - 0x000000B56E8FF95C 4第 4 个元素地址 - 第 3 个元素地址0x000000B56E8FF964 - 0x000000B56E8FF960 4第 5 个元素地址 - 第 4 个元素地址0x000000B56E8FF968 - 0x000000B56E8FF964 4第 6 个元素地址 - 第 5 个元素地址0x000000B56E8FF96C - 0x000000B56E8FF968 4从输出的结果来看每⼀⾏内部的每个元素都是相邻的地址之间相差 4 个字节跨⾏位置处的两个元素之间也是差 4 个字节所以⼆维数组中的每个元素都是连续存放的。3.4.1物理存储核心特征打破 “二维” 误区我们主观认为的存储方式按行、列的二维表格形式分散存储实际的存储方式所有元素在内存中连续不间断存储按 “行优先” 顺序依次排列无行、列的物理分隔四、C99 中的变⻓数组4.1核心定义变长数组Variable Length Array简称 VLA是 C99 标准新增的特性允许用变量指定数组的长度数组长度可在运行时根据变量值动态确定打破了 C99 前数组长度必须是「编译期常量」的限制。4.2示例#include stdio.h int main() { int n; printf(请输入数组长度); scanf(%d, n); int arr[n]; // 用变量n定义数组长度这就是变长数组 // 遍历赋值打印 for(int i0; in; i){ arr[i] i1; printf(arr[%d] %d\n, i, arr[i]); } return 0; }4.3注意事项避坑重点编译器支持问题GCC、Clang 完全支持 VLA但微软 MSVCVisual Studio 默认编译器不支持 C99 VLAVS2019 及以后可通过开启 C11 标准部分支持仍不推荐推荐切换为 Clang 编译器方式教程在结尾补充不可初始化定义变长数组时不能直接初始化int arr[n] {1,2,3};语法错误因为编译期无法确定长度编译器无法完成初始化栈溢出风险栈空间较小若变量值过大如int arr[1000000];会直接导致栈溢出程序崩溃sizeof 适配 VLAsizeof(变长数组)在运行时计算普通数组 sizeof 在编译期计算可正常获取 VLA 的实际总字节数求长公式依然可用。五、C 语言数组高频坑点与避坑技巧下标从 1 开始新手最易犯牢记数组下标从 0 开始最后一个元素下标为「长度 - 1」数组越界C 语言不检查后果严重拒绝硬编码长度用 sizeof 通用求长公式循环条件严格写「i 长度」定义数组时长度用变量C99 前限制C99 前编译器不支持如需动态长度可使用堆内存分配或 C99 变长数组未初始化局部数组直接使用随机值问题局部数组未初始化元素为随机值使用前必须手动初始化或赋值字符数组初始化的 \0 遗漏问题字符数组作为字符串使用时末尾必须有 \0否则字符串操作函数会出错二维数组初始化省略列长度语法错误二维数组列长度不可省略编译器需通过列长度确定每行元素个数六、二分查找练习#define _CRT_SECURE_NO_WARNINGS #includestdio.h int main() { int arr[] { 1,2,3,5,6,7,8,9,11,13 }; int right sizeof(arr) / sizeof(arr[0]) - 1;//右下标 int left 0;//左下标 int m 0;//中间下标 int k 7;//目标值 int flag 0;//标记判断能否找到0为找不到1为找到 while (left right) { m (right left) / 2; if (arr[m] k) { right m - 1; } if (arr[m] k) { left m 1; } else { flag 1; break; } } if (flag 1) { printf(下标为%d\n, m); } else printf(找不到); return 0; }七、补充1. sizeof 求长原理再梳理sizeof(数组名)计算整个数组占的总字节数sizeof(数组名[0])计算数组中单个元素的字节数数组元素类型一致取第一个即可相除结果就是数组实际的元素个数。例子#includestdio.h int main() { int num1[3] { 1, 2, 3 }; int sum sizeof(num1) / sizeof(num1[0]); printf(%d,sum); return 0; }2. 切换为 Clang 编译器完美支持 VLAClang 是 LLVM 的 C/C 编译器对 C99/C11/C17 标准支持完整能直接编译 VLA 代码步骤如下以 VS2022 为例VS2019 操作一致先安装 Clang打开 VS→创建 / 打开项目→顶部菜单栏「工具」→「获取工具和功能」→在弹出的安装界面中勾选「使用 Clang 构建的 C 开发工具」→点击「修改」完成安装切换项目编译器右键项目名称→「属性」→「配置属性」→「常规」→在「平台工具集」下拉框中选择「LLVM-vs2022」VS2019 为「LLVM-vs2019」→点击「确定」验证直接编写 VLA 代码如int n5; int arr[n];编译运行无报错完美支持。3. C 语言数组进阶用法数组与指针的关联核心等价公式数组作为函数参数传参本质 长度丢失问题字符数组与字符串的区别C 语言无字符串类型一维数组的经典操作逆序、查找、去重4. C 语言数组实战案例一维数组统计班级学生成绩平均分 / 最高分 / 最低分一维数组实现简单冒泡排序排序基础二维数组存储并打印矩阵行 / 列求和字符数组简单字符串遍历与统计字符个数 / 数字个数八、最后感谢大家对我文章的支持也很高兴与各位共同努力学习我会继续坚持分享总结共同奔赴下一个山海