如果要存 5 门课的成绩,按前面学的办法,只能声明 5 个变量:
#include <iostream>
int main() {
int score1, score2, score3, score4, score5;
std::cin >> score1 >> score2 >> score3 >> score4 >> score5;
int sum = score1 + score2 + score3 + score4 + score5;
std::cout << "总分:" << sum << std::endl;
return 0;
} 数组就是把一批同类型的数据,用一个名字 集中存起来,像一排连续编号的储物格。每个格子用下标区分, 借助下标,就能配合循环批量处理——这正是下一讲要做的事。
int scores[5]; // 声明一个能装 5 个 int 的数组,起名叫 scores | 部分 | 例子 | 含义 |
|---|---|---|
| 类型 | int | 数组里每个元素的类型,必须都一样 |
| 数组名 | scores | 整个数组的名字 |
| 长度 | [5] | 这个数组一共有 5 个格子 |
int scores[5]; 只是划出了 5 个格子,没有自动填 0 或任何默认值——
此时每个元素都是未初始化的,不能在赋值前读取:
int scores[5]; // 声明了,但没有赋值
std::cout << scores[0] << std::endl; // 错误示例:读取未初始化的 int int 属于未定义行为:
标准不保证程序会打印什么,甚至不保证程序还能按你写的顺序运行。
所以用数组前,一定要先用初始化列表赋值,或者逐个用 cin / 赋值填好,
不能把它理解成“只是得到一个不可预测的旧数字”。
0,不会是垃圾值:
int scores[5] = {90, 85}; // 只给了 2 个值
// scores[2]、scores[3]、scores[4] 会自动补成 0 Python:列表,长度可以随时变
scores = [90, 85, 70, 60, 95]
scores.append(100) # 随时加
C++:数组,长度声明时就定死
int scores[5] = {90, 85, 70, 60, 95};
不能再往里“加”一个(后面会学 vector)
声明的同时可以直接给出全部初始值,用花括号 { } 包住,逗号隔开。
访问某个元素,用数组名 + 中括号 + 下标——但下标是从
0 开始的,长度为 5 的数组,最后一个元素的下标是
4,不是 5。
#include <iostream>
int main() {
int scores[5] = {90, 85, 70, 60, 95};
std::cout << "第一个:" << scores[0] << std::endl;
std::cout << "第三个:" << scores[2] << std::endl;
std::cout << "最后一个:" << scores[4] << std::endl;
return 0;
} scores[5] 对一个长度为 5 的数组来说,根本不存在这个格子:
int scores[5] = {90, 85, 70, 60, 95};
std::cout << scores[5] << std::endl; // 危险!根本不存在下标 5
把 array_init.cpp 跑起来,再自己添加一行,打印第二个元素
(下标是几?)。再判断 scores[-1]、scores[4] 和
scores[5] 中哪些下标合法,并说明原因。不要通过“运行一下没崩溃”
来判断越界代码是否正确——未定义行为即使暂时没有明显现象,也仍然是错误。
-fsanitize=address,undefined -g。工具能帮忙定位错误,
但不能代替写出正确的下标范围。
std::cin >> scores[0]; 把输入的值存进下标 0 的格子;
scores[1] = scores[1] + 5; 则是先取出下标 1 的值、加上 5、
再存回同一个格子——和普通变量的读写规则完全一样,只是多了一层“选中哪个格子”的下标。
#include <iostream>
int main() {
int scores[3];
std::cout << "请输入 3 门课的成绩:" << std::endl;
std::cin >> scores[0];
std::cin >> scores[1];
std::cin >> scores[2];
scores[1] = scores[1] + 5; // 第二门课加 5 分(比如老师给了补偿分)
std::cout << "调整后:" << std::endl;
std::cout << scores[0] << " " << scores[1] << " " << scores[2] << std::endl;
return 0;
}
在 read_write_array.cpp 基础上,把数组长度从 3 改成 5,
对应地把 cin 的读入语句也补齐到 5 个,运行并验证。
int scores[5] 一旦声明,这个数组就永远是 5 个格子,
没有办法在中途“再多装一个”。如果运行时才知道要存多少数据(比如“这次考试有几个人报名不确定”),
数组就不太好用了——下节课先学会怎么用循环搭配数组批量处理数据,
模块五后面还会学一种长度可以随时变的容器:vector。
声明一个 int scores[4],用初始化列表直接赋值
{88, 92, 76, 100},分别打印下标 1 和下标
3 的元素。
声明 int prices[3],依次用 cin 读入 3 个价格,
不借助循环,直接把 prices[0] + prices[1] + prices[2] 算出来并打印。
声明一个 int scores[5] 并用初始化列表赋值,先完整打印一遍,
然后把下标 2 的元素改成 100,再完整打印一遍,
对比两次输出的差异。
| 能力 | 用到的工具 |
|---|---|
| 集中声明一批同类型数据 | 类型 数组名[长度]; |
| 声明时直接赋初值 | { 值1, 值2, … } |
| 读写某个元素 | 数组名[下标],下标从 0 开始 |
| 避免踩坑 | 下标不能超过“长度减 1”,否则越界 |
0、1、2……
如果数组有 30 个元素,难道要写 30 行吗?下节课把循环和数组结合起来,
用循环变量当下标,批量遍历、求和、找最大值最小值。