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

引用二维数组的某一格,要写两对方括号:数组名[行下标][列下标]。千万别写成 a[i, j]——那会变成 3.1.9 的逗号表达式,等价于 a[j],离你想取的格子十万八千里。遍历整张表就用两层 for:外管行、内管列。

📝 本节和前面几节的关系

7.2.1 把表订好了,这一节学"怎么取表里某一格"。a[i][j] 里的两个下标各自独立,每对方括号就是一次查找(第一对方括号先定位到某一行,第二对再定位到行里的某一格)——这正是 7.5.2"用指针遍历"能成立的原因。遍历它要两层循环,也就是 5.4 学过的"外圈一格 = 内圈一整圈"。

一、引用的写法

数组名[行下标][列下标]
int a[3][4] = {
    {85, 92, 78, 90},
    {60, 75, 88, 95},
    {100, 55, 70, 80}
};

a[0][0]       // 85  —— 第 0 行第 0 列
a[1][2]       // 88  —— 第 1 行第 2 列
a[2][3]       // 80  —— 最后一格

a[1][2] = 99; // 也能赋值(左值,3.4)

下标对照表(行列都从 0 起):

想要 写什么
第 1 行第 1 列 a[0][0]
第 i 行第 j 列 a[i-1][j-1]
最后一行最后一列 a[2][3](3 行 4 列时)
不存在的 a[3][0]、a[0][4] —— 越界

二、a[i, j] 为什么是坑

新手很容易把两个下标写成 a[i, j]。它能编过,但意思完全变了:

a[i, j]  ≡  a[j]      // 逗号取末值(3.1.9),只留下 j

而 a[j] 是"第 j 行整行"(在 7.5.2 会知道它其实是个地址),不是"第 i 行第 j 列"。你想取一个数,拿到的是一个地址——结果当然全错,而且不报错。

口诀:一行一对括号,两行两对。

三、遍历:两层 for 二次上岗

for (int i = 0; i < 行数; i++) {        // 外层:走行
    for (int j = 0; j < 列数; j++) {    // 内层:走列
        // 在这里用 a[i][j]
    }
}

这就是 5.4 说的"外圈一格 = 内圈一整圈":外层 i 每走一步,内层 j 就把这一行从左到右走完。所以总的"进去多少次" = 行 × 列。

四、动手 Demo:三种常见处理

#include <iostream>
using namespace std;

int main() {
    int a[3][4] = {{85, 92, 78, 90},
                   {60, 75, 88, 95},
                   {100, 55, 70, 80}};
    int rows = sizeof(a) / sizeof(a[0]);           // 3(7.2.1)
    int cols = sizeof(a[0]) / sizeof(a[0][0]);     // 4

    cout << "---- 原表 ----" << endl;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) cout << a[i][j] << "\t";
        cout << endl;
    }

    cout << "---- 每行总和 ----" << endl;
    for (int i = 0; i < rows; i++) {
        int rowSum = 0;
        for (int j = 0; j < cols; j++) rowSum += a[i][j];
        cout << "第 " << i + 1 << " 行 = " << rowSum << endl;
    }

    cout << "---- 全表最大值 ----" << endl;
    int mx = a[0][0];                              // 从第 0 行第 0 列起步
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            if (a[i][j] > mx) mx = a[i][j];
    cout << "最大 = " << mx << endl;
    return 0;
}

看到什么算成功:原表 3 行 4 列;每行总和依次是 345、318、305;最大值 100。

三个套路的要点:

任务 关键写法 注意
打印整表 双层循环 + \t 对齐 内层走完要 endl 换行
每行求和 rowSum 声明在外层、清 0 在外层 放错位置就会累加成"全表总和"
找全表最大 初值取 a[0][0],其余全比一遍 别取 0——全是负数时就错了

30 秒实验:把 int rowSum = 0; 挪到外层循环之前(两层循环外面),重跑。"每行总和"会变成 345、663、968——越滚越大,因为累加器再也没清零。这个错误极常见,值得亲手犯一次。

五、常见误区

  1. "a[i, j] 就是 a[i][j]"——不是。逗号运算只剩 j,取到的是整行地址而不是一格。两行两对括号。
  2. "外层行、内层列可以随我换"——语法上可以,但换完之后"每行求和"就变成了"每列求和"(数值会不一样)。先想清楚你要按行还是按列。
  3. "越界会报错"——和 7.1.2 一样,不报错。行下标到 rows、或列下标到 cols,都在悄悄踩内存。
  4. "求和变量放在哪都一样"——见上面的实验。"按行分组"的变量必须在行循环里清零。
自测三问(点开对答案) 1. 二维数组怎么取第 i 行第 j 列?——`a[i][j]`,**两对方括号**。 2. `a[i, j]` 等价于什么?——`a[j]`(逗号取末值),取到的是整行而不是一格。 3. "每行求总和"的累加变量应该声明在哪里?——在**外层(行)循环体内**,每进一行重新清零。

相关笔记