一维数组是一条线,二维数组是一张表(行列),三维数组是一摞表。多维数组在 C 语言里就是把多个方向的格子按顺序排进内存,配合嵌套循环访问。下面讲二维、三维数组怎么用。

逻辑上的 2×3 表 1 2 3 4 5 6 arr[r][c] 内存里按行优先排成一条 1 2 3 4 5 6 第 0 行排完再排第 1 行
二维数组在内存里还是一维连续:先排第 0 行、再排第 1 行(行优先)。

什么是多维数组

多维数组就是超过一维的数组。最常见的是二维(存表格、矩阵)和三维(存分层、体数据):

  • 二维数组用"行 + 列"存数据,适合表格和矩阵。
  • 三维数组用"层 + 行 + 列"存数据,适合分层或体块数据。

最快上手的方式是直接看声明和遍历:

#include <stdio.h>

int main() {
    /* 声明并初始化一个 2×2 的整数数组 */
    int arr[2][2] = { {10, 20}, {30, 40} };

    printf("2D Array Elements:\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

输出:

2D Array Elements:
10 20
30 40

声明语法

N 维数组的通用写法:

type arrName[size1][size2]....[sizeN];
  • type:数组里装的数据类型
  • arrName:数组名
  • size1..sizeN:每一维的大小

二维:

int arr[10][20];   /* 10 行 20 列 */

数组有多大

元素总数 = 各维相乘。内存占用 = 元素总数 × 单个元素大小:

  • int arr[10][20]:10 × 20 = 200 个元素
  • int 占 4 字节:200 × 4 = 800 字节

用 sizeof 也能算(在声明它的作用域里才行):

int arr[10][20];
size_t total = sizeof(arr);            /* 整个二维数组的字节数 */
size_t rows = sizeof(arr) / sizeof(arr[0]);      /* 10 */
size_t cols = sizeof(arr[0]) / sizeof(arr[0][0]); /* 20 */

二维数组的初始化

两种写法等价,推荐带内层花括号的,每一行清清楚楚:

int arr[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

/* 或 */
int arr[3][4] = { {0, 1, 2, 3},
                  {4, 5, 6, 7},
                  {8, 9, 10, 11} };

初始化按从左到右、从上到下填,每组内层花括号是一行。第一维可以省略,列数必须写出来,编译器要靠它算行的位置:

int arr[][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };   /* 3 行自动算出 */

初值个数 ≤ 数组总容量,缺的补 0。

访问元素

用"行索引 + 列索引",都是 0 起:

int arr[5] = {2, 4, 8, 12, 16};
/* 一维的访问复习一下 */
printf("%d\n", arr[2]);   /* 8 */

int mat[3][2] = { {0, 1}, {2, 3}, {4, 5} };
printf("%d\n", mat[1][0]);  /* 第二行第一个 = 2 */
printf("%d\n", mat[0][1]);  /* 第一行第二个 = 1 */

遍历二维数组

标准的做法是嵌套循环:外层管行,内层管列。

#include <stdio.h>

int main() {
    int arr[3][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 } };

    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 2; j++) {
            printf("arr[%d][%d]: %d   ", i, j, arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

输出:

arr[0][0]: 0   arr[0][1]: 1
arr[1][0]: 2   arr[1][1]: 3
arr[2][0]: 4   arr[2][1]: 5

改值也一样,比如 arr[0][0] = 1;

二维数组在内存里怎么排

内存是线性的,二维数组也得排成一条线。C 语言用的是行优先(row-major):先放完第一行,再放第二行。另一种叫列优先(column-major),先放完第一列,那是 Fortran 的做法,C 不用。

int mat[2][3] = {{1, 2, 3}, {4, 5, 6}};

内存顺序:1 2 3 4 5 6
         └第一行┘ └第二行┘

程序只需要记住数组的基地址,任何一个元素都能用"基地址 + 偏移"直接算出,这就是随机访问高效的底气。

把二维数组传给函数

传二维数组给函数,形参得写成指针数组,列数必须带上:

void print2D(int arr[][3], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int mat[2][3] = { {1, 2, 3}, {4, 5, 6} };
    print2D(mat, 2);
    return 0;
}

因为参数里的数组会退化成指针,编译器需要知道每行多宽(列数)才能算地址,所以列数不能省。行数传不传都行,反正循环里自己控制。

三维数组

三维 = 一组二维数组叠起来。声明:

type arr_name[x][m][n];      /* x 层,每层 m 行 n 列 */
int arr[2][2][2];            /* 2 层,每层 2×2 */

初始化,维数越多花括号越厚:

int arr[2][3][2] = { { {1, 1}, {2, 3}, {4, 5} },
                     { {6, 7}, {8, 9}, {10, 11} } };

第一维可省略,其余维必须写:

int arr[][3][2] = { /* ... */ };

访问用三个索引(层、行、列):

arr_name[d][i][j]

遍历要用三重循环:外层管层,中层管行,内层管列。

#include <stdio.h>

int main() {
    int arr[2][3][2] = { { {1, 1}, {2, 3}, {4, 5} },
                         { {6, 7}, {8, 9}, {10, 11} } };

    for (int i = 0; i < 2; ++i) {          /* 层 */
        for (int j = 0; j < 3; ++j) {      /* 行 */
            for (int k = 0; k < 2; ++k)    /* 列 */
                printf("arr[%d][%d][%d] = %d   ", i, j, k, arr[i][j][k]);
            printf("\n");
        }
        printf("\n\n");
    }
    return 0;
}

输出:

arr[0][0][0] = 1   arr[0][0][1] = 1
arr[0][1][0] = 2   arr[0][1][1] = 3
arr[0][2][0] = 4   arr[0][2][1] = 5


arr[1][0][0] = 6   arr[1][0][1] = 7
arr[1][1][0] = 8   arr[1][1][1] = 9
arr[1][2][0] = 10   arr[1][2][1] = 11

三维在内存里一层一层排:先存第一层(按行优先),再存第二层。传给函数时,除了第一维,其余维的大小都要在形参里写清楚,道理和二维一样。

什么时候用多维数组

  • 矩阵运算:线性代数、图像处理。
  • 表格数据:学生成绩表、库存表。
  • 游戏棋盘/地图:二维格子天然契合。
  • 体素数据:三维网格、CT 切片这类。

数据只要规则、方整、按行按列取,多维数组就是最顺手的容器。