数组=一排连号的储物柜:一个名字管着一串同类型的数据,每个格子有自己的编号(从 0 开始)。要存 50 个成绩,不用起 50 个变量名——一句 int score[50]; 就够了。
前六章你用的变量都是"一盒装一个值"(2.3 变量)。可现实里的数据经常是"一串":50 个成绩、100 个姓名、一张 3 行 4 列的表格。本章就是把"一个值"扩展成"成批的值"——数组、指针、引用,是同一件事(内存与地址)的三个侧面。数组是最容易上手的入口,7.4 起的指针才是本章的主菜。第五章的 for 循环(5.2)在这里终于找到了最般配的搭档:数遍一排柜子。
一、先看看"不用数组"有多难
想存 5 个学生的成绩,用前面的老办法得这样:
int s1 = 85, s2 = 92, s3 = 78, s4 = 66, s5 = 90;
int sum = s1 + s2 + s3 + s4 + s5; // 加 5 个还能忍
那如果是一个班 50 个人呢?
- 50 个变量名要一个个起,还得起得有意义(
s1…s50); - 求和要把 50 个名字全敲一遍;
- 想"找出最高分",得写 50 次比较;
- 最要命的是:换一个班、换一个年级,代码全得重写。
数组解决的正是这个:用一个名字 + 一个编号,代替一长串名字。
二、数组的三个特征
| 特征 | 含义 | 一句话记忆 |
|---|---|---|
| 同类型 | 一个数组里的所有格子必须是同一种类型(全是 int,或全是 double) |
一排柜子规格一样,才排得整齐 |
| 连续存放 | 在内存里一格挨着一格,中间没有空隙 | 连号柜子,挨着墙一溜儿排开 |
| 编号从 0 起 | 第一格是 a[0],第二格是 a[1]……第 n 格是 a[n-1] |
下标不是"第几个",是"偏移量" |
第三条是新手最容易摔跤的地方:长度为 5 的数组,合法下标是 0 1 2 3 4,没有 a[5]。这个"从 0 开始"的规矩贯穿整个 C/C++ 家族,本章后面所有的指针运算都建立在它上面。
三、数组在内存里长什么样
假设有 int a[5];(每格 4 字节),它大致是这样一片连续的内存:
a[0] a[1] a[2] a[3] a[4]
┌───────┬───────┬───────┬───────┬───────┐
│ │ │ │ │ │
└───────┴───────┴───────┴───────┴───────┘
地址 1000 1004 1008 1012 1016
偏移 0 1 2 3 4
关键读法:a[3] 不是"第 4 格的地址",而是"从开头 a 往后数 3 格"。这就是 7.4 讲指针时 a[i] 能写成 *(a + i) 的原因——下标本质上就是"偏移几个格子"。
三个可以直接验证的事实:
- 总字节数 = 每格字节数 × 格数,用 3.1.7 的
sizeof量:sizeof(a)得到 20; - 格数 =
sizeof(a) / sizeof(a[0])——这是不写死数字的"数格子"标准写法; - 首地址 =
a(也就是&a[0]),7.5 会展开讲。
四、解决一个问题要回答三件事(本节分工)
| 要做的事 | 问题 | 小节 |
|---|---|---|
| 声明 | 这排柜子放哪儿、多大、装什么 | 7.1.1 |
| 引用(访问) | 怎么打开第 i 格、怎么遍历 | 7.1.2 |
| 初始化 | 格子里的东西怎么一次摆好 | 7.1.3 |
五、动手 Demo:存 5 个成绩
#include <iostream>
using namespace std;
int main() {
int score[5] = {85, 92, 78, 66, 90}; // 初始化(7.1.3)
int sum = 0, maxScore = score[0];
for (int i = 0; i < 5; i++) { // 遍历:i 从 0 到 4(5.2 的 for)
cout << "第 " << i + 1 << " 个成绩 = " << score[i] << endl;
sum += score[i];
if (score[i] > maxScore) maxScore = score[i]; // 4.2.1 的单分支 if
}
cout << "总和 = " << sum << endl;
cout << "平均 = " << (double)sum / 5 << endl; // 3.3.2 的强制转换
cout << "最高 = " << maxScore << endl;
return 0;
}
看到什么算成功:逐行打印 5 个成绩,最后三行是 总和 = 411、平均 = 82.2、最高 = 92。
注意两个细节:① 循环条件是 i < 5 而不是 i <= 5——写成 <= 就会去访问 score[5],那是越界(7.1.2 专门讲这个坑);② 平均分要写成 (double)sum / 5,直接 sum / 5 会做整数除法得 82。
30 秒实验:把 int score[5] = {85, 92, 78, 66, 90}; 改成 int score[5];(不初始化),重新编译运行——你会看到 5 个乱七八糟的数(有的编译器给 0,有的给垃圾值)。这就是 2.3.2 说过的"未初始化=垃圾值",在数组上更明显:50 个格子都是垃圾。
六、常见误区
- "数组可以装不同类型的东西"——不行。
int a[5]里就只能放整数。想混装得靠 7.7 的指针数组或后面的结构体(第 8 章起)。 - "数组长度可以用变量"——标准 C++ 里不行。
int n = 5; int a[n];在 Dev-C++ 默认设置下多半编不过,长度必须是编译期就能确定的常量(7.1.1 细说)。 - "数组能整体赋值、整体输入输出"——不行。不能
cin >> a;也不能b = a;,必须一格一格来,通常配一层for。 - "
a[1]是第一个元素"——a[1]是第二个。第一个是a[0]。下标 = 从开头数几格,不是序号。
自测三问(点开对答案)
1. 数组的三个特征?——同类型、内存里连续存放、下标从 0 开始。 2. 长度 5 的数组,最后一个元素的下标是多少?——`4`。合法下标是 `0~4`。 3. 为什么说"下标本质是偏移量"?——因为 `a[i]` 的含义是"从头往后数 i 格",这也解释了为什么 `a[i]` 能写成 `*(a + i)`。- ☐ 7.1.1 一维数组的声明(下一节)
相关笔记
- 变量 —— 一盒一个值;数组=成排的盒子
- for循环语句 —— 遍历数组的最佳搭档
- sizeof运算符 —— 用它数出数组有多少格
- 小结 —— 上一章知识地图
- 🔷 C++基础 · 索引 —— 全课路线图