2-1 二维数组

2-1 二维数组

⏱ 本节预计用时:30分钟

本节意义:一维数组是一条线,能存一排数据。但现实中更多数据是"表格"------成绩表有行有列,棋盘是3x3,班级花名册一行一个学生。二维数组让你用一张表管理所有行和列,循环套循环,几行代码把全班的成绩算完。

本节目标:掌握二维数组的初始化和嵌套遍历;理解二维数组在内存中仍然是连续存储的。

1 开场------一维不够用了

上一节你用 char board[9] 存了井字棋的9个格子,一维数组一条线就搞定了。

但现在你是班主任,要管理5个学生、每个学生3门课的成绩。一维数组怎么存?

你可以这样:

c 复制代码
int scores[15] = {85, 90, 78,  92, 88, 95,  78, 82, 80,  80, 60, 72,  95, 91, 88};

15个数字排成一条线。但你得自己记住:前3个是学生0的,接下来3个是学生1的......一旦数据多了,这种"人脑分组"的方式极其容易出错。你想算"学生2的第2门课成绩"------得心算 2×3 + 1 = 7,然后去 scores[7] 里找。

这就是二维数组要解决的问题。

2 二维数组------一张有行有列的表

把刚才那15个成绩,用二维数组来存:

c 复制代码
int scores[5][3] = {
    {85, 90, 78},   // 学生0:语文 数学 英语
    {92, 88, 95},   // 学生1
    {78, 82, 80},   // 学生2
    {80, 60, 72},   // 学生3
    {95, 91, 88}    // 学生4
};

[5][3] 的意思是:5行、3列。第一维是行(学生),第二维是列(科目)。

现在你想查"学生2的第2门课成绩",直接写 scores[2][1],一目了然------不需要心算。

把二维数组想象成教学楼的储物柜墙 :5层楼(行),每层3个柜子(列)。scores[2][1] 就是第3层第2个柜子。

c 复制代码
         列0    列1    列2
       +------+------+------+
行0    |  85  |  90  |  78  |   学生0
       +------+------+------+
行1    |  92  |  88  |  95  |   学生1
       +------+------+------+
行2    |  78  |  82  |  80  |   学生2
       +------+------+------+
行3    |  80  |  60  |  72  |   学生3
       +------+------+------+
行4    |  95  |  91  |  88  |   学生4
       +------+------+------+

scores[2][1] → 行索引2、列索引1 → 82分。

3 嵌套遍历------两层循环管行管列

一维数组用一层循环,二维数组自然用两层:

c 复制代码
int scores[5][3] = { /* 刚才那堆数据 */ };

for (int i = 0; i < 5; i++) {           // i 管第几个学生(行)
    int sum = 0;
    for (int j = 0; j < 3; j++) {       // j 管第几门课(列)
        sum += scores[i][j];
    }
    printf("学生%d的平均分:%.1f\n", i + 1, (float)sum / 3);
}

外层循环 i 每走一次(换一个学生),内层循环 j 把3门课全跑一遍------这就叫"嵌套循环"。

运行情况:

如果你想反过来遍历------先按科目、再按学生:

c 复制代码
for (int j = 0; j < 3; j++) {           // j 管科目(列)
    int sum = 0;
    for (int i = 0; i < 5; i++) {       // i 管学生(行)
        sum += scores[i][j];
    }
    printf("科目%d的全班平均分:%.1f\n", j + 1, (float)sum / 5);
}

运行情况:

我们认真去想这个过程,就会发现,数组的元素都被遍历了,只是遍历的顺序不同而已。

遍历方式 外层循环 内层循环 结果
按学生 i(行) j(列) 每个学生的总分/平均分
按科目 j(列) i(行) 每门课的全班平均分

为了更好的理解整个过程是什么样的,我们可以让ai做一个可交互式执行代码的网页:

回到文件管理器,双击打开。

效果如下:

让ai把循环的流程图也加进去,可以再弄漂亮点,比如我给他说要蓝色系,大气简约。

得到了这样的东西(可以发给朋友装b了)。

是不是还挺有意思的?大家有想法都可以给ai说,have a try,看他能做出来什么样的东西!

4 内存里的二维数组

你可能以为二维数组在内存里也是一张二维的表。不是的。

内存是一维的------一条长长的地址线。二维数组只不过是"把第二行接在第一行后面"存在内存里。这一节叫"连续存储",二维也不例外。

以 int scores[2][3] = {{1,2,3}, {4,5,6}}; 为例:

c 复制代码
逻辑视图(你脑子里的表):         内存实际存储(一条线):

    列0  列1  列2              地址: 0 4 8 12 16 20
  +----+----+----+             +----+----+----+----+----+----+
  | 1  | 2  | 3  |  行0        | 1  | 2  | 3  | 4  | 5  | 6  |
  +----+----+----+             +----+----+----+----+----+----+
  | 4  | 5  | 6  |  行0        行0的3个      行1的3个
  +----+----+----+

scores[0][2](第1行第3列,值为3)和 scores[1][0](第2行第1列,值为4)在内存里是紧挨着的。

编译器计算 scores[i][j] 的地址:

c 复制代码
实际地址 = 首地址 + (i × 列数 + j) × 每个元素的大小

scores[1][2] → 首地址 + (1 × 3 + 2) × 4字节 = 首地址 + 20字节

5 总结

  1. 二维数组是"数组的数组"------int scores[5][3] 是5行3列的表 ------ 第一维是行(学生),第二维是列(科目)。scores[2][1] 就是第3个学生的第2门课。

  2. 遍历用两层循环:外层管行,内层管列 ------ 按学生遍历:i在外、j在内。按科目遍历:j在外、i在内。scores[i][j] 本身不用改,只换循环顺序。

  3. 二维数组在内存里仍是连续存储------一行接一行 ------ scores[i][j] 的地址 = 首地址 + (i × 列数 + j) × 元素大小。所以声明时列数不能省略。

6 练习题

题目:4 行 2 列数组,输入数据,找出最大值,输出最大值、所在行、列下标。

c 复制代码
#include <stdio.h>
int main() {
    int arr[4][2];
    int max, x=0, y=0;
    for(int i=0;i<4;i++) {
        for(int j=0;j<2;j++) {
            scanf("%d", &arr[i][j]);
        }    
    }    
    max = arr[0][0];
    for(int i=0;i<4;i++) {
        for(int j=0;j<2;j++) {
            if(arr[i][j] > max) {
                max = arr[i][j];
                x = i; y = j;
            }
        }
    }
    printf("最大值=%d,行:%d,列:%d", max, x, y);
    return 0;
}

运行情况:

相关推荐
书生执笔画浮沉5 小时前
Linux桌面端应用向鸿蒙PC迁移适配纪实
c++·qt·c·信创·海思·libc·ohos
带土17 小时前
15. sscanf 函数详解
c
C++ 老炮儿的技术栈1 天前
sizeof操作符
c语言·c++·人工智能·mfc·c
多弗朗皮卡丘3 天前
数据结构番外:环形队列
数据结构·c
91刘仁德3 天前
传输层协议深度拆解:端口寻址、UDP 报文与 TCP 可靠性机制全解析
linux·网络·c++·网络协议·tcp/ip·udp·c
C++ 老炮儿的技术栈4 天前
不一样的数值交换
数据结构·c++·人工智能·算法·c·csdn开发云
C++ 老炮儿的技术栈4 天前
main函数之后的调用
c语言·c++·人工智能·qt·mfc·c
C++ 老炮儿的技术栈6 天前
有符号变量与无符号变量的区别
服务器·开发语言·前端·数据结构·c++·算法·c
haliu6 天前
【FHE 同态加密】我们如何实现同态加密推理(十六):未完成清单与已知边界(同态加密推理的精度、性能与安全)
人工智能·嵌入式·c·fhe·推理引擎·c11·边缘推理·同态加密推理