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

二维数组的初始化有两种写法:分行写({{1,2,3},{4,5,6}},看得清每行)和不分行写({1,2,3,4,5,6},按行优先顺序一路填下去)。没写满的格子仍然自动补 0,= {} 依然是把整张表刷 0 的最短写法。唯一的硬规矩:列数不能省,行数可以。

📝 本节和前面几节的关系

上一节讲"怎么取一格",这一节讲"怎么把整张表一次摆好"。规则是 7.1.3 那一套的直接推广:花括号、自动补 0、{} 清零——只是多了一层嵌套。"分行"与"不分行"的等价性,靠的正是 7.2.1 讲的行优先布局:不分行写法就是"把行优先那一排原样抄下来"。

一、写法 1:分行写(推荐)

int a[3][4] = {
    {85, 92, 78, 90},        // 第 0 行
    {60, 75, 88, 95},        // 第 1 行
    {100, 55, 70, 80}        // 第 2 行
};

外层一对花括号包住整张表,里层每对花括号包住一行。好处是看得见行——哪几个数是一行的,一目了然。写成绩表、矩阵都推荐这种。

二、写法 2:不分行写(按行优先铺开)

int b[3][4] = {85, 92, 78, 90, 60, 75, 88, 95, 100, 55, 70, 80};

效果和上面完全一样。因为它就是按 7.2.1 的内存顺序一路填:第 0 行 4 格 → 第 1 行 4 格 → 第 2 行 4 格。

缺点很明显:一眼看不出哪几个数是一行。只适合给很短的、或全 0 的表用。超过一行的数据,尽量分行写。

三、写法 3:填一部分,其余补 0

在"分行"的基础上,每行也可以只写一部分:

int c[3][4] = {
    {1},              // {1, 0, 0, 0}
    {4, 5},           // {4, 5, 0, 0}
    {7, 8, 9}         // {7, 8, 9, 0}
};

每一行没写完的格子补 0,而不是"补下一行的数"。这一点和"不分行只写一半"不一样:

int d[3][4] = {1, 2, 3};      // {1,2,3,0, 0,0,0,0, 0,0,0,0}
                              // 前三个数全在第 0 行!

这是本节最容易搞错的一处:不分行时,没写的数不会自动分给第 1、2 行,整张表在第 0 行就被填了 3 格,后面 9 格全是 0。

四、写法 4:整张表清零

int  e[3][4] = {};            // 整张表 12 格全 0
char g[19][19] = {};          // 围棋盘全空

和 7.1.3 一样,空花括号 = 全刷 0,不是"什么都不做"。这是最常用的"开局清干净"写法。

五、行数可省、列数不可省

int f[][4] = {{1,2,3,4}, {5,6,7,8}};      // ✅ 行数自动算出 2
int h[3][] = {{1,2,3,4}, {5,6,7,8}};      // ❌ 列数不能省,编译报错

为什么列数不能少?因为编译器必须知道"一行有多长"(就是 7.2.1 的"每行 16 字节"),才能算出 a[i][j] 到底在哪儿;行数则可以从花括号数据里数出来。

六、动手 Demo:四种形态对照

#include <iostream>
using namespace std;

void print2D(int a[][4], int rows) {          // 列数不能省,7.8.3 详解
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) cout << a[i][j] << "\t";
        cout << endl;
    }
    cout << endl;
}

int main() {
    int a[3][4] = {{85,92,78,90},{60,75,88,95},{100,55,70,80}};
    int b[3][4] = {1, 2, 3};
    int c[3][4] = {{1},{4,5},{7,8,9}};
    int d[3][4] = {};

    cout << "分行全填:" << endl;  print2D(a, 3);
    cout << "不分行填三个:" << endl; print2D(b, 3);
    cout << "分行各填一部分:" << endl; print2D(c, 3);
    cout << "空花括号:" << endl;  print2D(d, 3);
    return 0;
}

看到什么算成功:

分行全填:
85  92  78  90
60  75  88  95
100 55  70  80

不分行填三个:
1   2   3   0
0   0   0   0
0   0   0   0

分行各填一部分:
1   0   0   0
4   5   0   0
7   8   9   0

空花括号:
0   0   0   0
0   0   0   0
0   0   0   0

重点看第二块:{1,2,3} 全落在第 0 行,其余 9 格是 0。第三块则每行各留了点 0——"分行"和"不分行"的差别,全在这里。

30 秒实验:把 int b[3][4] = {1, 2, 3}; 改成 int b[3][4] = {1, 2, 3, 4, 5};,重跑第二块——变成第 0 行 1 2 3 4、第 1 行第 0 列 5、其余全 0。自己顺着"行优先"数一遍,就能彻底记住这个规矩。

七、常见误区

  1. "不分行只写三个数,会一行分一个"——不会。从头按行优先一路填,前三个全在第 0 行。
  2. "分行写时,某行不写满会接着填下一行"——不会。每行各自补 0,行与行之间互不影响。
  3. "行数列数都能省"——只有行数能省(a[][4])。列数必须写,因为它决定"一行多长"。
  4. "= {} 是没初始化"——是全部置 0。不初始化是 int a[3][4];(垃圾值),两者完全不同。
  5. "花括号的层数无所谓"——层数对了才是"分行"。少写一层勉强能编过(就是不分行写法),但含义会变。
自测三问(点开对答案) 1. `int a[3][4] = {1, 2};` 之后 `a[1][0]` 是几?——`0`。两个数都在第 0 行,其余全补 0。 2. `int a[][4] = {{1,2,3,4},{5,6,7,8}};` 合法吗?——合法,**行数自动算出 2**。但列数 `4` 不能省。 3. 把 3×4 数组整张清零的最短写法?——`int a[3][4] = {};`

相关笔记