🔷 第七章 数组、指针和引用
📄 一句话概括

数组=一排连号的储物柜:一个名字管着一串同类型的数据,每个格子有自己的编号(从 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 个人呢?

数组解决的正是这个:用一个名字 + 一个编号,代替一长串名字。

二、数组的三个特征

特征 含义 一句话记忆
同类型 一个数组里的所有格子必须是同一种类型(全是 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) 的原因——下标本质上就是"偏移几个格子"。

三个可以直接验证的事实:

四、解决一个问题要回答三件事(本节分工)

要做的事 问题 小节
声明 这排柜子放哪儿、多大、装什么 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 个格子都是垃圾。

六、常见误区

  1. "数组可以装不同类型的东西"——不行。int a[5] 里就只能放整数。想混装得靠 7.7 的指针数组或后面的结构体(第 8 章起)。
  2. "数组长度可以用变量"——标准 C++ 里不行。int n = 5; int a[n]; 在 Dev-C++ 默认设置下多半编不过,长度必须是编译期就能确定的常量(7.1.1 细说)。
  3. "数组能整体赋值、整体输入输出"——不行。不能 cin >> a; 也不能 b = a;,必须一格一格来,通常配一层 for。
  4. "a[1] 是第一个元素"——a[1] 是第二个。第一个是 a[0]。下标 = 从开头数几格,不是序号。
自测三问(点开对答案) 1. 数组的三个特征?——同类型、内存里连续存放、下标从 0 开始。 2. 长度 5 的数组,最后一个元素的下标是多少?——`4`。合法下标是 `0~4`。 3. 为什么说"下标本质是偏移量"?——因为 `a[i]` 的含义是"从头往后数 i 格",这也解释了为什么 `a[i]` 能写成 `*(a + i)`。

相关笔记