C语言从0入门(十)|二维数组详解与矩阵实战

大家好,我是网域小星球。

前面我们学习了一维数组,能够批量存储一组同类型数据;但在实际编程和工程场景中,经常需要处理二维或多维结构的数据,比如班级成绩表(行=学生、列=科目)、矩阵运算、地图网格、图像像素等。

C语言通过二维数组实现这种"行列结构"的数据存储,写法规范、访问高效,是入门阶段必须掌握的重要知识点。本篇从二维数组的本质、定义、初始化、遍历,到经典矩阵实战案例全面展开,全程适配VS2022,代码可直接编译运行,零基础也能轻松吃透。

目录

一、本章学习目标

二、二维数组的基本概念

[1. 什么是二维数组?](#1. 什么是二维数组?)

[2. 二维数组的核心特点](#2. 二维数组的核心特点)

三、二维数组的定义与初始化

[1. 定义格式(标准规范)](#1. 定义格式(标准规范))

[2. 初始化方式(3种常用,重点掌握)](#2. 初始化方式(3种常用,重点掌握))

(1)完全初始化(最规范,推荐使用)

(2)部分初始化(未赋值元素自动补0)

(3)省略行数初始化(推荐,灵活高效)

[3. 初始化注意事项(新手必记)](#3. 初始化注意事项(新手必记))

四、二维数组的访问与遍历

[1. 元素访问方式(核心语法)](#1. 元素访问方式(核心语法))

[2. 二维数组的遍历(核心操作,必掌握)](#2. 二维数组的遍历(核心操作,必掌握))

[3. 遍历实战案例(VS2022可直接运行)](#3. 遍历实战案例(VS2022可直接运行))

五、二维数组经典实战案例(全部适配VS2022)

案例1:打印3×3矩阵(基础入门)

案例2:求二维数组(矩阵)所有元素的总和

案例3:查找矩阵中最大值及其位置

案例4:矩阵转置(高频考点)

案例5:打印二维字符数组图形(趣味实战)

六、二维数组常见错误(避坑指南,新手必看)

七、本章核心总结

下期预告

一、本章学习目标

学完本篇你将彻底掌握:

  1. 二维数组的本质:"行+列"的表格结构,本质是"一维数组的数组"。

  2. 二维数组的定义、初始化、赋值的完整规范。

  3. 使用双重for循环遍历二维数组的方法(外层控行、内层控列)。

  4. 二维数组在内存中的存储方式(行优先存储)。

  5. 经典实战案例:矩阵打印、矩阵求和、矩阵转置、二维数组查找。

  6. 新手避坑指南:下标越界、维度混乱、遍历边界错误等问题排查。


二、二维数组的基本概念

1. 什么是二维数组?

二维数组的本质是**"一维数组的数组"**,可以形象理解为一张"表格"------由若干行、若干列组成,每一行都是一个独立的一维数组,所有行的列数相同。

示例:定义一个3行4列的二维整数数组

cpp 复制代码
int arr[3][4]; // 3行4列,共3×4=12个元素,可存放12个整数

形象表格示意:

列0 列1 列2 列3
arr[0][0] arr[0][1] arr[0][2] arr[0][3]
arr[1][0] arr[1][1] arr[1][2] arr[1][3]
arr[2][0] arr[2][1] arr[2][2] arr[2][3]

2. 二维数组的核心特点

  • 所有元素数据类型统一:只能存放同一种类型的数据(如全部是int、全部是char)。

  • 行数和列数固定:定义时必须指定列数,行数可省略(由初始化列表决定)。

  • 内存连续存储:二维数组在内存中是"行优先"存储(先存第0行,再存第1行,依次类推),本质是一块连续的内存空间。

  • 下标从0开始:行下标和列下标都从0开始,最大行下标=行数-1,最大列下标=列数-1。


三、二维数组的定义与初始化

1. 定义格式(标准规范)

cpp 复制代码
数据类型 数组名[行数][列数];

说明:

  • 数据类型:数组中所有元素的类型(int、char、double等)。

  • 数组名:遵循变量命名规则(字母、数字、下划线组成,不数字开头,不使用关键字),建议见名知意(如成绩表用score、矩阵用mat)。

  • 行数:二维数组的行数(可选,可省略,由初始化列表决定)。

  • 列数:二维数组的列数(必填,不能省略,否则编译器报错)。

示例:

cpp 复制代码
int score[5][3];  // 5行3列,存放5个学生、3门科目的成绩
char map[10][10]; // 10行10列,存放地图网格数据
double mat[4][4]; // 4行4列,存放浮点型矩阵

2. 初始化方式(3种常用,重点掌握)

二维数组的初始化,核心是"按行赋值",可分为完全初始化、部分初始化、省略行数初始化三种,未赋值的元素会自动补0(整数型)。

(1)完全初始化(最规范,推荐使用)

用大括号包裹,每一行的元素单独用大括号包裹,清晰直观,不易出错。

cpp 复制代码
// 3行4列,完全初始化,每一行单独赋值
int arr[3][4] = {
    {10, 20, 30, 40},  // 第0行
    {50, 60, 70, 80},  // 第1行
    {90, 0, 10, 20}    // 第2行
};
(2)部分初始化(未赋值元素自动补0)

只给部分行、部分元素赋值,未赋值的元素(包括未赋值的行)会自动初始化为0,适合大部分元素为0的场景(如矩阵)。

cpp 复制代码
// 3行4列,部分初始化
int arr[3][4] = {
    {1, 2},        // 第0行:前2个元素为1、2,后2个补0
    {},            // 第1行:所有元素补0
    {9, 8, 7}      // 第2行:前3个元素为9、8、7,最后1个补0
};
// 等价于:{{1,2,0,0}, {0,0,0,0}, {9,8,7,0}}
(3)省略行数初始化(推荐,灵活高效)

定义时可省略行数,编译器会根据初始化列表的"行数",自动计算数组的行数,但列数不能省略(核心易错点)。

cpp 复制代码
// 省略行数,编译器自动识别为3行4列(有3个大括号,代表3行)
int arr[][4] = {
    {1,2,3,4},
    {5,6},
    {7}
};
// 等价于:int arr[3][4] = {{1,2,3,4}, {5,6,0,0}, {7,0,0,0}}

3. 初始化注意事项(新手必记)

  • 列数不能省略:二维数组定义时,无论是否初始化,列数都必须指定(如int arr[][4]合法,int arr[3][]非法)。

  • 初始化列表的行数不能超过定义的行数:如int arr[3][4],初始化列表最多只能有3个大括号(3行)。

  • 未赋值元素自动补0:仅针对全局二维数组和初始化时未赋值的元素,局部二维数组未初始化时,元素为随机垃圾值。

  • 不能整体赋值:二维数组不能直接用"arr2 = arr1"赋值,必须通过循环逐个元素赋值。


四、二维数组的访问与遍历

1. 元素访问方式(核心语法)

二维数组的元素通过"行下标+列下标"访问,语法格式:

cpp 复制代码
数组名[行下标][列下标];

注意:行下标和列下标都从0开始,最大行下标=行数-1,最大列下标=列数-1,超出范围会导致下标越界(程序崩溃或乱码)。

示例:

cpp 复制代码
int arr[2][2] = {{1,2}, {3,4}};
printf("%d\n", arr[0][0]); // 访问第0行第0列,输出1
printf("%d\n", arr[1][0]); // 访问第1行第0列,输出3
arr[1][1] = 10;            // 修改第1行第1列的元素,变为10
printf("%d\n", arr[1][1]); // 输出10

2. 二维数组的遍历(核心操作,必掌握)

二维数组的遍历,必须使用双重for循环:外层循环控制"行",内层循环控制"列",逐个访问每一个元素。

遍历模板(通用,直接套用):

cpp 复制代码
// 假设二维数组为int arr[行数][列数];
for (int i = 0; i < 行数; i++) // 外层循环:遍历每一行(i为行下标)
{
    for (int j = 0; j < 列数; j++) // 内层循环:遍历当前行的每一列(j为列下标)
    {
        // 访问当前元素arr[i][j],可进行打印、赋值等操作
        printf("%d ", arr[i][j]);
    }
    printf("\n"); // 每遍历完一行,换行(避免所有元素打印在一行)
}

3. 遍历实战案例(VS2022可直接运行)

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    // 定义并初始化一个3行4列的二维数组
    int arr[3][4] = {
        {1,2,3,4},
        {5,6,7,8},
        {9,0,1,2}
    };
    
    // 双重for循环遍历二维数组
    printf("二维数组遍历结果:\n");
    for (int i = 0; i < 3; i++) // 外层控行(3行)
    {
        for (int j = 0; j < 4; j++) // 内层控列(4列)
        {
            printf("%d ", arr[i][j]); // 打印当前元素,加空格分隔
        }
        printf("\n"); // 换行
    }
    
    return 0;
}

运行结果:

cpp 复制代码
二维数组遍历结果:
1 2 3 4
5 6 7 8
9 0 1 2

五、二维数组经典实战案例(全部适配VS2022)

二维数组最常用的场景是"矩阵操作",以下案例覆盖作业、笔试高频考点,代码可直接复制运行,重点掌握思路。

案例1:打印3×3矩阵(基础入门)

需求:定义一个3×3的整型矩阵,初始化后,按表格形式打印输出。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    // 定义并初始化3×3矩阵
    int mat[3][3] = {
        {1,2,3},
        {4,5,6},
        {7,8,9}
    };
    
    printf("3×3矩阵:\n");
    // 遍历打印
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("%d\t", mat[i][j]); // \t 制表符,让输出更整齐
        }
        printf("\n");
    }
    
    return 0;
}

案例2:求二维数组(矩阵)所有元素的总和

思路:遍历所有元素,用一个变量累加求和。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    int arr[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,0,1,2}};
    int sum = 0; // 用于存放总和
    
    // 遍历所有元素,累加求和
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            sum += arr[i][j];
        }
    }
    
    printf("二维数组所有元素的总和为:%d\n", sum);
    return 0;
}

运行结果:48(1+2+3+4+5+6+7+8+9+0+1+2=48)

案例3:查找矩阵中最大值及其位置

思路:假设第一个元素为最大值,遍历所有元素,逐个比较,更新最大值及其行、列下标。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    int mat[3][4] = {{10,20,30,40}, {50,60,70,80}, {90,0,10,20}};
    int max = mat[0][0]; // 假设第一个元素为最大值
    int row = 0, col = 0; // 存放最大值的行、列下标
    
    // 遍历所有元素,查找最大值
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if (mat[i][j] > max)
            {
                max = mat[i][j]; // 更新最大值
                row = i;        // 更新行下标
                col = j;        // 更新列下标
            }
        }
    }
    
    printf("矩阵中的最大值为:%d\n", max);
    printf("最大值所在位置:第%d行,第%d列(下标从0开始)\n", row, col);
    return 0;
}

运行结果:

cpp 复制代码
矩阵中的最大值为:90
最大值所在位置:第2行,第0列(下标从0开始)

案例4:矩阵转置(高频考点)

需求:将3×3矩阵转置(行变列、列变行),原矩阵的第i行第j列元素,转置后变为第j行第i列元素。

原矩阵: 转置后矩阵:

cpp 复制代码
1 2 3          1 4 7
4 5 6    →     2 5 8
7 8 9          3 6 9
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    // 原矩阵(3×3)
    int src[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
    // 目标矩阵(用于存放转置后的结果)
    int dest[3][3];
    
    // 矩阵转置:src[i][j] → dest[j][i]
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            dest[j][i] = src[i][j];
        }
    }
    
    // 打印原矩阵
    printf("原矩阵:\n");
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("%d\t", src[i][j]);
        }
        printf("\n");
    }
    
    // 打印转置后矩阵
    printf("\n转置后矩阵:\n");
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("%d\t", dest[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

案例5:打印二维字符数组图形(趣味实战)

需求:用二维字符数组定义一个5×5的爱心图形,打印输出。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
    // 定义5×5二维字符数组,存放爱心图形
    char heart[5][5] = {
        {' ', '*', '*', ' ', ' '},
        {'*', '*', '*', '*', ' '},
        {'*', '*', '*', '*', '*'},
        {' ', '*', '*', '*', ' '},
        {' ', ' ', '*', ' ', ' '}
    };
    
    // 遍历打印爱心
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            printf("%c", heart[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

运行结果(爱心图形):

cpp 复制代码
 **  
**** 
*****
 *** 
  *  

六、二维数组常见错误(避坑指南,新手必看)

  1. 下标越界:访问的行下标≥行数,或列下标≥列数(如int arr[3][4],访问arr[3][4]),会导致程序崩溃、乱码,或运行结果异常。

  2. 省略列数定义:定义二维数组时,省略列数(如int arr[3][]),编译器直接报错,列数必须指定。

  3. 遍历边界错误:外层循环条件写成i ≤ 行数(如3行写成i ≤ 3),导致下标越界,正确应为i < 行数。

  4. 混淆行和列:把行下标和列下标写反(如arr[j][i]写成arr[i][j]),导致访问错误元素,结果异常。

  5. 局部二维数组未初始化:局部二维数组未赋值时,元素为随机垃圾值,直接使用会导致结果异常,需手动初始化。

  6. 整体赋值错误:用"arr2 = arr1"给二维数组赋值,编译器报错,必须通过双重循环逐个元素赋值。


七、本章核心总结

  1. 二维数组的本质是"一维数组的数组",呈"行+列"的表格结构,用于存储二维数据。

  2. 定义格式:数据类型 数组名[行数][列数],列数不能省略,行数可省略(由初始化列表决定)。

  3. 初始化有3种方式:完全初始化、部分初始化、省略行数初始化,未赋值元素自动补0。

  4. 访问方式:数组名[行下标][列下标],下标从0开始,避免越界。

  5. 遍历必须用双重for循环:外层控行、内层控列,是二维数组操作的核心。

  6. 经典应用:矩阵打印、求和、查找、转置,是作业和笔试的高频考点。


下期预告

下一篇我们将进入C语言最难但最重要的核心知识点------指针,从指针的本质、定义、解引用,到指针与变量的关系,逐步拆解,避开新手误区,搭配实战案例,全程VS2022精讲,为后续指针进阶打下基础。


✅ 本篇配套练习:打印5×5二维数组随机矩阵、求二维数组每列的和、完成4×4矩阵转置

相关推荐
汀、人工智能2 小时前
[特殊字符] 第77课:最长递增子序列
数据结构·算法·数据库架构·图论·bfs·最长递增子序列
网域小星球2 小时前
C 语言从 0 入门(十五)|综合小项目:菜单交互与简易功能实现
c语言·开发语言·交互
澈2072 小时前
堆排序:高效构建大顶堆实战
数据结构·算法·排序算法
网域小星球2 小时前
C 语言从 0 入门(十六)|动态内存管理:malloc /free/calloc /realloc 精讲
c语言·开发语言·free·malloc·动态内存
chh5632 小时前
C++--内存管理
java·c语言·c++·windows·学习·面试
我真不是小鱼2 小时前
cpp刷题打卡记录27——无重复字符的最长子串 & 找到字符串中所有字母的异位词
数据结构·c++·算法·leetcode
XuecWu33 小时前
原生多模态颠覆Scaling Law?解读语言“参数需求型”与视觉“数据需求型”核心差异
人工智能·深度学习·算法·计算机视觉·语言模型
We་ct3 小时前
LeetCode 69. x 的平方根:两种解法详解
前端·javascript·算法·leetcode·typescript·平方
一直不明飞行3 小时前
C++:string,写法s.find(‘@‘) != s.end()是否有问题
开发语言·c++·算法