Kodewick
第 11 课,共 30 课目录

数组

第 11 课,共 30 课 · 第 2 部分 · 函数和数组 · 约 15 分钟

一周有七天,每天走了多少步,要用七个变量吗?不用。数组是一排同类型的变量,共用一个名字,按位置编号。

main.c
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] 和普通变量一样,能读也能改:

main.c
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。

main.c
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 一步一步变大:

main.c
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 课讲过)。

找最大值

先假定第一个最大,再从第二个起一个一个比:遇到更大的,就换成它。顺便记下它的下标,就知道是哪一天。

main.c
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] 是一个元素占的字节数,两个一除就是元素个数。

main.c
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}

只写 int a[5]; 不给花括号,这几个元素和没赋值的变量一样,里面是什么不知道,先赋值再用。

复制数组

数组不能用 = 整个赋给另一个数组。要复制,就用循环一个一个地复制。复制完,两个数组各管各的:改 week2 不会影响 week1。

main.c
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 里最常见的错之一。

main.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 算,就不会对不上。

练一练

写好代码,点检查。提示随便看,实在想不出来再看答案。

练习 1

最低温度

找出 temps 里最低的温度,显示 最低:-3。

要显示
最低:-3
exercise1.c
1#include <stdio.h>2 3int main(void) {4    int temps[6] = {4, -1, 2, -3, 0, 5};5 6    return 0;7}
输出
点「运行」或按 Ctrl+Enter
练习 2

及格的人数

数一数 scores 里有几个分数大于等于 60,显示 及格:4 人。用 sizeof 算数组有几个元素。

要显示
及格:4 人
exercise2.c
1#include <stdio.h>2 3int main(void) {4    int scores[] = {58, 72, 91, 45, 60, 83};5 6    return 0;7}
输出
点「运行」或按 Ctrl+Enter
练习 3

倒着显示

修好这个循环,让它从最后一个元素倒着显示到第一个,一行一个。现在它越界了。

要显示
5
4
3
2
1
exercise3.c
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}
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • 数组是一排同类型的元素:int a[5] = {...};,不写大小就照花括号里的个数。
  • 下标从 0 开始,n 个元素的下标是 0 到 n - 1。
  • 用 for (int i = 0; i < n; i++) 走一遍,可以求和、求平均、找最大最小。
  • sizeof a / sizeof a[0] 是元素个数;给的值不够,剩下的是 0。
  • 数组不能用 = 整个复制;用循环一个一个地复制。
  • 越界是未定义行为,C 不会替你检查。循环条件写 i < n。