数组
一周有七天,每天走了多少步,要用七个变量吗?不用。数组是一排同类型的变量,共用一个名字,按位置编号。
1#include <stdio.h>2 3int main(void) {4 int steps[7] = {4200, 6100, 5300, 8000, 7200, 9900, 3500};5 printf("周一:%d 步\n", steps[0]);6 printf("周日:%d 步\n", steps[6]);7 return 0;8}
周一:4200 步 周日:3500 步
int steps[7] 声明一个能放 7 个 int 的数组;花括号里是一开始放进去的值。每个值叫一个元素,方括号里的数叫下标。点单步,可以看到数组画成一排格子,每格下面标着它的下标。
下标从 0 开始
第一个元素的下标是 0,不是 1。7 个元素的数组,下标是 0 到 6。steps[2] 和普通变量一样,能读也能改:
1#include <stdio.h>2 3int main(void) {4 int scores[3] = {78, 85, 90};5 printf("第一个:%d\n", scores[0]);6 scores[1] = 88;7 scores[2] += 5;8 printf("%d %d %d\n", scores[0], scores[1], scores[2]);9 return 0;10}
第一个:78 78 88 95
下标也可以是变量或者算式,比如 scores[i]、scores[i + 1]。这正是数组好用的地方。
用循环走一遍
用 for 循环,让 i 从 0 走到 4,就能依次拿到每个元素。注意条件是 i < 5,不是 i <= 5。
1#include <stdio.h>2 3int main(void) {4 double temps[5] = {21.5, 23.0, 19.8, 25.2, 22.1};5 for (int i = 0; i < 5; i++) {6 printf("第 %d 天:%.1f 度\n", i + 1, temps[i]);7 }8 return 0;9}
第 1 天:21.5 度 第 2 天:23.0 度 第 3 天:19.8 度 第 4 天:25.2 度 第 5 天:22.1 度
printf 不能一下子显示整个数组,只能像这样一个一个地显示。
求和与平均
先放一个 0,循环里把每个元素加上去,就是总数。点单步,看 total 一步一步变大:
1#include <stdio.h>2 3int main(void) {4 int steps[7] = {4200, 6100, 5300, 8000, 7200, 9900, 3500};5 int total = 0;6 for (int i = 0; i < 7; i++) {7 total += steps[i];8 }9 printf("一周:%d 步\n", total);10 printf("平均每天:%.1f 步\n", (double)total / 7);11 return 0;12}
一周:44200 步 平均每天:6314.3 步
total 是 int,除以 7 会去掉小数,所以先用 (double) 转一下(第 4 课讲过)。
找最大值
先假定第一个最大,再从第二个起一个一个比:遇到更大的,就换成它。顺便记下它的下标,就知道是哪一天。
1#include <stdio.h>2 3int main(void) {4 int steps[7] = {4200, 6100, 5300, 8000, 7200, 9900, 3500};5 int best = steps[0];6 int day = 0;7 for (int i = 1; i < 7; i++) {8 if (steps[i] > best) {9 best = steps[i];10 day = i;11 }12 }13 printf("最多的一天是第 %d 天:%d 步\n", day + 1, best);14 return 0;15}
最多的一天是第 6 天:9900 步
数组有多大
上面的循环把 7 写死了。数组改成 8 个元素,每个 7 都得跟着改。sizeof 能算出来:sizeof prices 是整个数组占的字节数,sizeof prices[0] 是一个元素占的字节数,两个一除就是元素个数。
1#include <stdio.h>2 3int main(void) {4 int prices[] = {12, 8, 25, 4};5 int count = sizeof prices / sizeof prices[0];6 printf("整个数组:%zu 字节\n", sizeof prices);7 printf("一个元素:%zu 字节\n", sizeof prices[0]);8 printf("一共 %d 个\n", count);9 return 0;10}
整个数组:16 字节 一个元素:4 字节 一共 4 个
方括号里不写大小,编译器会照花括号里有几个值来定。sizeof 的结果要用 %zu 显示。
花括号里的值比数组小,剩下的元素是 0:
这段程序显示什么?
1#include <stdio.h>2 3int main(void) {4 int a[5] = {3, 1};5 printf("%d %d\n", a[1], a[4]);6 return 0;7}
a[1] 是第二个值 1。只给了两个值,a[2] 到 a[4] 都是 0。所以 int a[5] = {0}; 是把整个数组清零的常见写法。
只写 int a[5]; 不给花括号,这几个元素和没赋值的变量一样,里面是什么不知道,先赋值再用。
复制数组
数组不能用 = 整个赋给另一个数组。要复制,就用循环一个一个地复制。复制完,两个数组各管各的:改 week2 不会影响 week1。
1#include <stdio.h>2 3int main(void) {4 int week1[3] = {5, 7, 6};5 int week2[3];6 for (int i = 0; i < 3; i++) {7 week2[i] = week1[i];8 }9 week2[0] = 9;10 printf("%d %d\n", week1[0], week2[0]);11 return 0;12}
5 9
越界
5 个元素的数组,最后一个是 a[4]。写成 i <= 5,循环会去拿 a[5]:那个位置不属于这个数组。C 不会替你检查,真正的程序可能读到一个乱七八糟的数,可能改坏别的变量,也可能直接崩溃,这是未定义行为。这是 C 里最常见的错之一。
1#include <stdio.h>2 3int main(void) {4 int scores[5] = {78, 85, 90, 66, 72};5 int total = 0;6 for (int i = 0; i <= 5; i++) {7 total += scores[i];8 }9 printf("总分:%d\n", total);10 return 0;11}
index 5 out of bounds for type 'int [5]'发生了什么: 停在第 7 行:你访问了 scores[5],可 scores 只有 5 格(scores[0]~scores[4])。
我们的引擎会在出事的那一行停下来,告诉你哪个下标越界了。点「看看是怎么出错的」,或者点单步走到最后,看 i 变成 5 的那一刻。
小技巧: 循环条件写 i < 个数,不要写 i <= 个数。个数用 sizeof 算,就不会对不上。
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。
最低温度
✓找出 temps 里最低的温度,显示 最低:-3。
最低:-3
1#include <stdio.h>2 3int main(void) {4 int temps[6] = {4, -1, 2, -3, 0, 5};5 6 return 0;7}
及格的人数
✓数一数 scores 里有几个分数大于等于 60,显示 及格:4 人。用 sizeof 算数组有几个元素。
及格:4 人
1#include <stdio.h>2 3int main(void) {4 int scores[] = {58, 72, 91, 45, 60, 83};5 6 return 0;7}
倒着显示
✓修好这个循环,让它从最后一个元素倒着显示到第一个,一行一个。现在它越界了。
5 4 3 2 1
1#include <stdio.h>2 3int main(void) {4 int a[5] = {1, 2, 3, 4, 5};5 for (int i = 5; i >= 0; i--) {6 printf("%d\n", a[i]);7 }8 return 0;9}
这一课学会了
- 数组是一排同类型的元素:
int a[5] = {...};,不写大小就照花括号里的个数。 - 下标从
0开始,n个元素的下标是0到n - 1。 - 用
for (int i = 0; i < n; i++)走一遍,可以求和、求平均、找最大最小。 sizeof a / sizeof a[0]是元素个数;给的值不够,剩下的是0。- 数组不能用
=整个复制;用循环一个一个地复制。 - 越界是未定义行为,C 不会替你检查。循环条件写
i < n。