Appearance
数组:一批数据放一起
预计学习时长:约 2.5 小时(仅 L1 主线,建议分两次完成)· 运行环境:VS2026 / gcc / clang 均可(VLA 例外,见 L2)
🎯 学习目标
学完本讲,你将能够:
- 创建并初始化一维、二维数组,说清"元素类型必须相同、个数不能为 0"两条规则
- 用 for 循环配合下标
[]完成数组的输入、遍历、输出 - 用
sizeof(arr)/sizeof(arr[0])自动计算元素个数,不再手写死数字 - 通过打印地址的实验,说出"数组在内存中连续存放"这一结论
- 独立写出"数组找最大值""二分查找"两个经典程序
🔗 先修知识
- 第 6 讲:循环:数组的遍历完全依赖 for 循环
- 第 3 讲:数据类型和变量:数组元素就是一个个变量
- 第 4 讲:运算符详解:
/和%在二分查找里出场
先修自检(三问)
for (int i = 0; i < 5; i++)的循环体执行几次?i 的最大值是多少?- 用 scanf 读一个 int 时,参数为什么要写
&x? - 数组元素能不能一半是 int、一半是 char?(本讲第一节回答)
答不上来先回去快速复习,再回来继续。
一、数组是什么
想象你要保存全班 20 个人的数学成绩。不用数组,你得写 20 个变量:
c
int s1, s2, s3, /* ……还有 17 个 */ s20;名字起不完,也没法用循环批量处理。数组就是同类型元素排成一排、统一编号的集合:
c
int math[20]; // 20 个 int,编号 0~19从概念里能挖出两条规则:
| 规则 | 含义 |
|---|---|
| 元素个数不能为 0 | 数组至少装 1 个数据 |
| 元素类型必须相同 | 一个数组里只能全是 int,或全是 char…… |
数组按"排数"分类:上面这排叫一维数组;把"一维数组"再排成一排,就是二维数组;二维以上统称多维数组,实际开发中二维最常见。
📘 提高(L2):数组本身也是一种类型
int arr1[10]; 中,去掉数组名,剩下的 int [10] 就是这个数组的类型。注意 int [10] 和 int [12] 是两个不同的类型——元素个数也是类型的一部分。这个说法现在只需了解,等学到函数传参时它会再次出场。
二、一维数组的创建和初始化
2.1 创建
🧩 示意片段 · 仅说明语法,不能单独编译
c
type arr_name[常量值];type:元素类型,可以是 char、short、int、double,也可以是以后学的自定义类型arr_name:数组名,起得有实际意义即可[]里的常量值:数组的大小(元素个数),按实际需求定
c
int math[20]; // 20 个数学成绩
char ch[8]; // 8 个字符
double score[10]; // 10 个双精度成绩2.2 初始化
创建的同时给定初始值,叫初始化,用大括号把值列表包起来:
c
int a[5] = {1, 2, 3, 4, 5}; // 完全初始化:5 个值对 5 个位置
int b[6] = {1}; // 不完全初始化:b[0]=1,其余自动补 0
int c[10] = {0}; // 全 0 初始化,最常用的一行三条高频规则:
- 初始化时数组大小可以省略——编译器按值的个数自动定:
int d[] = {1,2,3};就是长度 3; - 值的个数不能超过大小:
int e[3] = {1,2,3,4};直接编译报错(too many initializer values); - 没给初值的局部数组内容是随机的:
int f[5];里全是垃圾值,想"从 0 开始"务必= {0}。
📜 标准卡(L2 选读):不完全初始化补 0
- C 标准:C11 §6.7.9p21——初始化列表没覆盖到的静态存储期对象补 0;§6.7.9p31——局部数组同样自动补 0,不是垃圾值。
- gcc 16 / MSVC:行为一致,剩余元素补 0。
- 结论:
int b[6] = {1};得到{1,0,0,0,0,0}是 🟢 标准保证。
2.3 下标:给每个元素编号
C 语言规定数组下标从 0 开始。n 个元素的数组,下标范围是 0 ~ n-1,最后一个元素的下标是 n-1。访问元素用下标引用操作符 []:
c
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
arr[7] // 下标 7,是第 8 个元素,值 8
arr[3] // 下标 3,第 4 个元素,值 4⚠️ 下标 7 对应第 8 个元素——"下标 = 序号 - 1",这个换算要练到条件反射。
三、一维数组的使用
3.1 打印数组
思路只有一句话:用 for 循环产生 0~9 所有下标,逐个访问。
📄 print_arr.c · ✅ 完整程序(可直接复制编译)
c
#include <stdio.h>
int main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}text
1 2 3 4 5 6 7 8 9 103.2 从键盘输入数组
同理,循环产生下标,配合 scanf。注意 &arr[i] 的 & 一个都不能少——scanf 要的是地址:
📄 input_arr.c · ✅ 完整程序(可直接复制编译)
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = {0};
for (int i = 0; i < 10; i++)
{
scanf("%d", &arr[i]);
}
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}输入 10 个整数(空格分隔),原样打印回来。
四、一维数组在内存中怎么摆放
数组元素在内存里到底长什么样?做实验:把每个元素的地址都打印出来。
📄 addr_1d.c · ✅ 完整程序(可直接复制编译)
c
#include <stdio.h>
int main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 0; i < 10; i++)
{
printf("&arr[%d] = %p\n", i, (void *)&arr[i]);
}
return 0;
}在某台机器上的输出(你的机器地址值不同,但规律相同):
text
&arr[0] = 0061F1DC
&arr[1] = 0061F1E0
&arr[2] = 0061F1E4
&arr[3] = 0061F1E8
...观察两点:
- 下标越大,地址越大;
- 相邻元素地址相差 4——正好是一个 int 的字节数。
结论:数组在内存中是连续存放的,像一排紧挨着的储物格。这个结论此刻只是"记住",到第 12 讲:指针时它会成为指针遍历数组的理论基础。
五、sizeof 计算数组元素个数
遍历数组时,循环上限"10"是手写死的。数组一改成 20 个元素,就得跟着改所有循环——容易漏改。C 语言给了一个自动计算的办法:
📄 sizeof_arr.c · ✅ 完整程序(可直接复制编译)
c
#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("整个数组占 %zu 字节\n", sizeof(arr)); // 40
printf("一个元素占 %zu 字节\n", sizeof(arr[0])); // 4
printf("元素个数是 %zu\n", sizeof(arr) / sizeof(arr[0])); // 10
return 0;
}原理:数组总字节数 ÷ 单个元素字节数 = 元素个数。sizeof 是第 4 讲介绍过的关键字,这里直接对数组名使用即可。
从此循环上限可以这样写,数组怎么变都不用改代码:
c
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++) ...⚠️ 提前打个招呼:
sizeof(arr)只在数组定义的那个函数里好使。数组一旦作为参数传给函数,这个算法会悄悄失灵——原因到第 8 讲:函数揭开。
六、二维数组
6.1 概念与创建
把一维数组当元素排成一排,就是二维数组。创建语法:
🧩 示意片段 · 仅说明语法,不能单独编译
c
type arr_name[常量值1][常量值2];c
int arr[3][5]; // 3 行,每行 5 个元素,元素是 int
double data[2][8]; // 2 行,每行 8 个元素,元素是 doubleint arr[3][5]; 读法:一共 3 行,每行 5 个 int,总共 15 个元素、占 60 字节。
6.2 二维数组的初始化
同样用大括号,四种写法各有用途:
c
// ① 不完全初始化:前两个是 1、2,其余全部补 0
int arr1[3][5] = {1, 2};
int arr2[3][5] = {0}; // 全 0,最常用
// ② 完全初始化:15 个值按行顺序连着写
int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
// ③ 按行初始化:每行一个括号,缺的位置补 0(推荐,最清晰)
int arr4[3][5] = {{1,2}, {3,4}, {5,6}};
// ④ 省略行,但绝不能省略列
int arr5[][5] = {1, 2, 3};
int arr6[][5] = {1,2,3,4,5,6,7};
int arr7[][5] = {{1,2}, {3,4}, {5,6}};写法④里为什么行能省、列不能省?编译器按"每行 5 个"去切分值的列表:arr6 有 7 个值,5 个一行切完是"第一行 5 个 + 第二行 2 个",行数 2 是算出来的;要是列也不知道,就切不动了。
6.3 二维数组的下标与遍历
行、列都从 0 开始,锁定行号和列号就锁定唯一元素。int arr[3][5] 中 arr[2][4] 是第 3 行第 5 列。
📄 arr2d_io.c · ✅ 完整程序(可直接复制编译)
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[3][5] = {1, 2, 3, 4, 5,
2, 3, 4, 5, 6,
3, 4, 5, 6, 7};
// 输出单个元素:第 3 行第 5 列
printf("arr[2][4] = %d\n", arr[2][4]);
// 双层循环遍历整个二维数组
for (int i = 0; i < 3; i++) // 行号
{
for (int j = 0; j < 5; j++) // 列号
{
printf("%d ", arr[i][j]);
}
printf("\n"); // 每行打完换行
}
return 0;
}text
arr[2][4] = 7
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7把内层 printf 换成 scanf("%d", &arr[i][j]); 就是二维数组输入,规律完全一样。
6.4 二维数组在内存中怎么摆放
和一维一样做地址实验:
📄 addr_2d.c · ✅ 完整程序(可直接复制编译)
c
#include <stdio.h>
int main()
{
int arr[3][5] = {0};
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("&arr[%d][%d] = %p\n", i, j, (void *)&arr[i][j]);
}
}
return 0;
}输出里能发现:每行内部相邻差 4;跨行的两个元素(arr[0][4] 和 arr[1][0])之间也差 4。
结论:二维数组整体是一整块连续空间,按"先行后列"的顺序一个挨一个摆放,并不是"一行一块、行与行之间隔开"。
七、综合示例一:字符从两端向中间汇聚
需求:屏幕上先显示一串 #,然后两端的 # 逐个被真实字符替换,向中间靠拢,像"解密"动画。这是数组 + 循环的经典组合练习。
📄 converge.c · ✅ 完整程序(可直接复制编译;用了 Windows 的 Sleep,VS 与 MinGW gcc 均可,Linux/macOS 见页尾 L3)
c
#include <stdio.h>
#include <string.h>
#include <windows.h> // Sleep 函数在这里声明
int main()
{
char arr1[] = "welcome to bit..."; // 目标内容,17 个字符
char arr2[] = "#################"; // 显示内容,17 个 #
int left = 0;
int right = (int)strlen(arr1) - 1; // 最后一个字符的下标
printf("%s\n", arr2);
while (left <= right)
{
Sleep(500); // 暂停 500 毫秒,看清过程
arr2[left] = arr1[left]; // 左端替换
arr2[right] = arr1[right]; // 右端替换
left++;
right--;
printf("%s\n", arr2);
}
return 0;
}运行效果(每行间隔半秒):
text
#################
# #
## ##
### ……逐行向中间逼近
welcome to bit...思路拆解:两个"指针式"下标 left、right 分别从两端向中间走,每轮把 arr2 两端各替换一个字符。left <= right 控制走到中间就停——字符串下标从 0 到 strlen-1,别忘了结尾还有个 \0 不参与。
八、综合示例二:二分查找
问题:在一个升序数组里找指定数字,找到打印下标,找不到打印"找不到"。
最笨的办法是从头到尾遍历,n 个数最多比 n 次。猜价格的游戏你肯定玩过:不超过 300 元,你第一刀肯定猜 150 而不是 1——每次砍掉一半,这就是二分查找(折半查找)。
📄 bsearch.c · ✅ 完整程序(可直接复制编译)
c
#include <stdio.h>
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int key = 7; // 要找的数字
int left = 0;
int right = (int)(sizeof(arr) / sizeof(arr[0])) - 1; // 最右下标
int mid = 0;
int find = 0;
while (left <= right)
{
mid = left + (right - left) / 2; // 中间下标(安全写法,见下)
if (arr[mid] > key)
right = mid - 1; // 目标在左半边
else if (arr[mid] < key)
left = mid + 1; // 目标在右半边
else
{
find = 1; // 正好命中
break;
}
}
if (find)
printf("找到了,下标是 %d\n", mid);
else
printf("找不到\n");
return 0;
}text
找到了,下标是 610 个数最多查 4 次(10→5→2→1),而遍历最多要 7 次;数据一多,差距是数量级的。
📘 提高(L2):mid 为什么写成 left + (right - left) / 2
直觉写法 (left + right) / 2 在 left、right 都很大时,left + right 可能超出 int 上限——有符号加法溢出是 🔴 未定义行为。left + (right - left) / 2 先算差值,永远不会溢出,两种写法在正常数据下结果相同。建议从第一天起就使用安全写法。
⚠️ 常见坑与报错(本讲汇总)
| 你看到的现象 | 原因 | 修法 |
|---|---|---|
| too many initializer values | 初始化值的个数超过数组大小 | 删多余值,或加大数组、省略大小让编译器数 |
| 数组内容是奇怪的垃圾值 | 只声明没初始化:int arr[5]; | 写成 int arr[5] = {0}; |
| 输出多一个乱码 / 程序崩溃 | 下标越界:arr[10] 访问了 0~9 之外的位置 | 循环上限用 i < sz;越界不报错但结果是 🔴 UB |
| VS 报 error C2133: 未知大小 | 用了变长数组 int arr[n];(n 是变量) | VS 不支持 VLA,见下方 L2;改用固定大小或 malloc |
#include <stido.h> 找不到文件 | 把 stdio 拼成了 stido | 检查拼写;VS 报 "Cannot open include file" |
| scanf 读数组后数据全是 0 | &arr[i] 写成了 arr[i] | scanf 要地址,& 不能丢 |
| 数组传进函数后 sizeof 算出的个数不对 | 函数里数组名退化(第 8 讲细讲) | 暂时把元素个数当参数一起传进去 |
🛠️ 动手练习
规则:先独立做,卡住了再依次展开提示,不要一上来就看答案。
📊 本讲网页练习进度0 / 5(0%)
进度自动保存在本浏览器;编程题不计数,请在编辑器中完成。
练习 1 · ⭐ 基础 · 选择判断
知识点:数组的基本规则
题干:下列说法正确的是?(多选)
练习 2 · ⭐ 基础 · 读输出
知识点:不完全初始化 + sizeof
题干:写出输出:
📄 ex2.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
int arr[6] = {1};
printf("%d %d %d\n", arr[0], arr[3], arr[5]);
printf("%zu\n", sizeof(arr) / sizeof(arr[0]));
return 0;
}请写出两行输出(数字间空格,两行用分号隔开):
空①
练习 3 · ⭐ 基础 · 读输出
知识点:二维数组按行初始化
题干:写出输出:
📄 ex3.c · ✅ 完整程序
c
#include <stdio.h>
int main()
{
int arr[3][5] = {{1, 2}, {3, 4}, {5, 6}};
printf("%d %d\n", arr[1][1], arr[2][4]);
return 0;
}请写出输出:
空①
练习 4 · ⭐ 基础 · 找错改错
知识点:越界与 sizeof 时机
题干:下面代码有两处问题,请填出修正:
🧩 待改错片段
c
int arr[10] = {0};
for (int i = 0; i <= 10; i++)
{
arr[i] = i;
}第一空:循环条件应改为 ____;第二空:想把上限写成"自动随数组大小变化",i < 后面应填 ____。
空①空②
练习 5 · ⭐ 基础 · 程序填空
知识点:scanf 读数组
题干:补全从键盘读入 5 个整数到数组的代码(两个空):
🧩 待补全片段
c
int arr[5] = {0};
for (int i = 0; i < 5; i++)
{
scanf("______", ______);
}第一空填格式串(含引号),第二空填第二个参数:
空①空②
练习 6 · ⭐⭐ 提高 · 写小程序
知识点:数组找最大值
🛠 动手编程题 · 请在 VS2026(或你的编辑器)中完成
创建一个长度为 5 的整型数组,从键盘输入 5 个整数,找出其中的最大值并打印。
✅ 过关标准:输入 3 7 2 9 4 输出 9;再测 5 5 5 5 5 与 -1 -5 -2。
网页内无法练习写代码,亲手敲、亲手编译才能真正学会。下方按顺序展开 思路 → 步骤 → 答案。
💡 提示 1(思路)
先假设第 0 个元素最大,然后从第 1 个开始逐个"打擂台"。
🧭 提示 2(步骤)
int max = arr[0];- for 循环 i 从 1 到 4:
if (arr[i] > max) max = arr[i]; - 打印 max
🔑 参考答案与解释
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[5] = {0};
for (int i = 0; i < 5; i++)
{
scanf("%d", &arr[i]);
}
int max = arr[0];
for (int i = 1; i < 5; i++)
{
if (arr[i] > max)
max = arr[i];
}
printf("最大值是 %d\n", max);
return 0;
}思路:max 是"擂台冠军",每个元素上台挑战一次。冠军初始值必须是 arr[0] 而不是 0——否则全是负数时会错。
练习 7 · ⭐⭐ 提高 · 写小程序
知识点:二维数组遍历累加
🛠 动手编程题 · 请在 VS2026(或你的编辑器)中完成
定义一个 3×4 的二维数组并用 1~12 初始化,计算所有元素的平均值并打印,保留 2 位小数。
✅ 过关标准:输出 平均值是 6.50。
网页内无法练习写代码,亲手敲、亲手编译才能真正学会。下方按顺序展开 思路 → 步骤 → 答案。
💡 提示 1(思路)
双层循环求总和,元素个数用 3×4 或两层计数都行。
🧭 提示 2(步骤)
int arr[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };- 双层循环累加 sum
double avg = (double)sum / 12;注意强转,否则整除printf("%.2f", avg);
🔑 参考答案与解释
c
#include <stdio.h>
int main()
{
int arr[3][4] = {{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}};
int sum = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
sum += arr[i][j];
}
}
double avg = (double)sum / 12;
printf("平均值是 %.2f\n", avg);
return 0;
}坑:(double)sum / 12 的强转不能少——78 / 12 整除得 6,先转成 double 才有 6.5。这和第 3 讲的整除坑是同一个。
练习 8 · ⭐⭐⭐ 挑战 · 改造程序
知识点:二分查找封装成函数
🛠 动手编程题 · 请在 VS2026(或你的编辑器)中完成
把本讲的二分查找改写成独立函数 `int binary_search(int arr[], int sz, int key)`:找到返回下标,找不到返回 -1。main 里调用它,在 1~10 的数组中查 7 和查 11。
✅ 过关标准:查 7 输出 找到了,下标是 6;查 11 输出 找不到。
网页内无法练习写代码,亲手敲、亲手编译才能真正学会。下方按顺序展开 思路 → 步骤 → 答案。
💡 提示 1(思路)
把 while 循环整体搬进函数,命中就 return mid,循环自然结束还没命中就 return -1。
🧭 提示 2(步骤)
- 函数头照抄题目,left=0、right=sz-1
- 循环体用安全写法求 mid
- 命中 return mid;循环结束 return -1
- main 里用返回值 >= 0 判断
🔑 参考答案与解释
c
#include <stdio.h>
int binary_search(int arr[], int sz, int key)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] > key)
right = mid - 1;
else if (arr[mid] < key)
left = mid + 1;
else
return mid;
}
return -1;
}
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sz = (int)(sizeof(arr) / sizeof(arr[0]));
int idx = binary_search(arr, sz, 7);
if (idx >= 0)
printf("找到了,下标是 %d\n", idx);
else
printf("找不到\n");
return 0;
}注意:函数里没法用 sizeof(arr)/sizeof(arr[0]) 算个数(数组传进函数会"退化",第 8 讲详讲),所以必须把 sz 当参数传进去。
📝 小结与自测
本讲主线:数组 = 同类型、连续存放、从 0 编号的一排格子。一维用一层循环遍历,二维用两层循环(外层行、内层列);个数交给 sizeof(arr)/sizeof(arr[0]);内存里它们都是紧挨着的一整块。
自测三问(能不看笔记答出来才算过关):
int arr[3][5] = { {1,2}, {3,4} };里arr[1][2]的值是多少?为什么?- 为什么找最大值的初始值必须是
arr[0]而不是 0? - 二分查找的前提条件是什么?
(left+right)/2有什么隐患?
🔬 选学拓展(L3)
以下内容超出主线要求
适合学有余力或对跨平台、底层感兴趣的同学,不影响后续学习。
L3-1:让"汇聚动画"跨平台跑
本讲的 Sleep(500) 来自 windows.h,只有 Windows 能用。跨平台思路是用标准库 time.h 的 clock() 自己"忙等":
📄 converge_portable.c · ✅ 完整程序(可直接复制编译,任何平台)
c
#include <stdio.h>
#include <string.h>
#include <time.h>
void wait_ms(int ms)
{
clock_t start = clock();
while ((clock() - start) * 1000.0 / CLOCKS_PER_SEC < ms)
{
/* 空转等待 */
}
}
int main()
{
char arr1[] = "welcome to bit...";
char arr2[] = "#################";
int left = 0;
int right = (int)strlen(arr1) - 1;
printf("%s\n", arr2);
while (left <= right)
{
wait_ms(500);
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
printf("%s\n", arr2);
}
return 0;
}忙等会占满一个 CPU 核做无意义循环,只适合演示动画这种轻量场景;真正的定时问题等学到操作系统内容再说。
L3-2:变长数组(VLA)——C99 的新特性与 VS 的拒绝
C99 之前,[] 里只能写常量、常量表达式,或靠初始化省略大小。C99 引入了变长数组(variable-length array):允许用变量指定大小。
c
int n = a + b;
int arr[n]; // 长度运行时才知道三个要点:
- 长度只有运行时才能确定,所以不能初始化;
- "变长"指创建时用变量定长度,一旦创建大小就固定了,不会伸缩;
- 好处是不必拍脑袋估大小,运行时按需分配精确长度。
📄 vla_demo.c · ✅ 完整程序(gcc/clang 可编译;VS 不支持 VLA,会报 error C2133)
c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n); // 根据输入决定数组大小
int arr[n]; // 变长数组:只有 gcc/clang 认
for (int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}📜 标准卡(L2 选读):VLA 的地位
- C 标准:C99 §6.7.5.2 引入 VLA;C11 §6.7.5.2p6 起改为可选特性(附录 J),编译器可以不实现。
- gcc:完整支持(本程序实测可编译运行)。
- MSVC(VS):从未支持,
int arr[n];报 error C2133。 - 结论:VLA 是 🟡 可选特性,跨平台代码不要用;需要运行时定长,等学完第 15 讲:动态内存管理用 malloc。
L3-3:越界为什么"有时不报错"
arr[10] 越界访问一个只有 0~9 的数组,程序可能照常运行——C 语言不做下标越界检查(🔴 UB,标准明文规定不诊断)。它实际读写的是数组旁边的内存:可能改了别的变量,可能碰坏函数返回地址。调试器里给越界位置设数据断点、或开启 AddressSanitizer(gcc -fsanitize=address)都能抓到。这个坑会一路伴随 C 语言学习,第 12 讲指针处还会正面遭遇。