C语言二维数组的使用详解
目录
- 二维数组的基本概念
- 定义与声明
- 内存布局
- 数组大小计算
- 二维数组的初始化
- 完全初始化
- 部分初始化
- 省略行数的初始化
- 二维数组的访问与操作
- 下标访问
- 循环遍历
- 二维数组与指针
- 二维数组作为函数参数
- 传二维数组到函数
- 动态二维数组
- 常见应用场景
- 矩阵运算
- 游戏开发
- 图像处理
- 注意事项
- 总结
二维数组是C语言中处理表格、矩阵等结构化数据的重要工具。它本质上是一种特殊的一维数组,其中每个元素又是一个一维数组。下面将详细介绍二维数组的使用方法、内存布局、访问方式及常见应用场景。
二维数组的基本概念
定义与声明
二维数组的声明需要指定数据类型、行数和列数:
// 声明一个3行4列的整型二维数组
int matrix[3][4];// 声明并初始化
int matrix2[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};
内存布局
C语言中,二维数组在内存中是连续存储的,按行优先(Row-major order)排列:
// 逻辑上的二维数组
int matrix[2][3] = {{1, 2, 3},{4, 5, 6}
};// 实际内存布局(连续存储)
// 1 2 3 4 5 6
数组大小计算
二维数组占用的总内存大小可以通过以下公式计算:
总字节数 = 行数 × 列数 × sizeof(数据类型)int matrix[3][4];
size_t totalBytes = sizeof(matrix); // 3×4×4 = 48字节
size_t rowBytes = sizeof(matrix[0]); // 4×4 = 16字节
size_t elementBytes = sizeof(matrix[0][0]); // 4字节
二维数组的初始化
完全初始化
为每个元素提供初始值:
// 完整初始化
int matrix[2][3] = {{1, 2, 3},{4, 5, 6}
};// 省略内部花括号(按行优先填充)
int matrix2[2][3] = {1, 2, 3, 4, 5, 6};
部分初始化
只提供部分初始值,未指定的元素自动初始化为0:
// 部分初始化示例
int matrix[3][4] = {{1, 2}, // 第一行: 1, 2, 0, 0{5, 6, 7}, // 第二行: 5, 6, 7, 0{9} // 第三行: 9, 0, 0, 0
};// 只初始化第一个元素
int matrix2[2][3] = {{1}}; // 只有matrix2[0][0]=1,其余为0
省略行数的初始化
声明时可以省略行数,但必须指定列数,编译器会根据初始值自动推断行数:
// 自动推断行数为3
int matrix[][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};// 不规则行数的初始化
int matrix2[][3] = {{1, 2}, // 自动补0: 1, 2, 0{5, 6, 7},{9} // 自动补0: 9, 0, 0
}; // 行数为3
二维数组的访问与操作
下标访问
通过行下标和列下标访问二维数组元素,下标从0开始:
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};// 访问元素
int a = matrix[0][1]; // 第一行第二列,值为2
int b = matrix[1][2]; // 第二行第三列,值为6// 修改元素
matrix[0][0] = 100; // 第一行第一列改为100
循环遍历
通常使用嵌套循环遍历二维数组:
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};// 按行遍历
for (int i = 0; i < 2; i++) {for (int j = 0; j < 3; j++) {printf("%d ", matrix[i][j]);}printf("\n");
}// 输出结果:
// 1 2 3
// 4 5 6
二维数组与指针
二维数组名可以视为指向第一个元素(即第一行)的指针:
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};// matrix是指向第一行的指针
int (*rowPtr)[3] = matrix; // 指向包含3个int的数组// 通过指针访问元素
int element = *(*(rowPtr + 1) + 2); // 等价于matrix[1][2],值为6// 指针遍历
for (int i = 0; i < 2; i++) {for (int j = 0; j < 3; j++) {printf("%d ", *(*(rowPtr + i) + j));}printf("\n");
}
二维数组作为函数参数
传二维数组到函数
函数接收二维数组时,必须指定列数,但行数可以省略:
// 函数声明方式1:指定行数和列数
void printMatrix1(int matrix[2][3], int rows, int cols) {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {printf("%d ", matrix[i][j]);}printf("\n");}
}// 函数声明方式2:省略行数
void printMatrix2(int matrix[][3], int rows) {// ...同上...
}// 函数声明方式3:使用指针
void printMatrix3(int (*matrix)[3], int rows) {// ...同上...
}// 调用示例
int main() {int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};printMatrix1(matrix, 2, 3);printMatrix2(matrix, 2);printMatrix3(matrix, 2);return 0;
}
动态二维数组
对于需要动态确定大小的二维数组,可以使用指针数组或二级指针:
#include <stdlib.h>// 方法1:指针数组(每行独立分配)
int** createMatrix1(int rows, int cols) {int** matrix = (int**)malloc(rows * sizeof(int*));for (int i = 0; i < rows; i++) {matrix[i] = (int*)malloc(cols * sizeof(int));}return matrix;
}// 方法2:连续内存块(更高效)
int* createMatrix2(int rows, int cols) {return (int*)malloc(rows * cols * sizeof(int));
}// 使用示例
int main() {// 方法1int** matrix1 = createMatrix1(2, 3);matrix1[0][0] = 1;// ...free(matrix1[0]);free(matrix1[1]);free(matrix1);// 方法2int* matrix2 = createMatrix2(2, 3);matrix2[0] = 1; // 第一行第一列matrix2[3] = 4; // 第二行第一列(3=1*3+0)// ...free(matrix2);return 0;
}
常见应用场景
矩阵运算
二维数组最典型的应用是矩阵运算:
#include <stdio.h>// 矩阵加法
void matrixAdd(int A[][3], int B[][3], int C[][3], int rows, int cols) {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {C[i][j] = A[i][j] + B[i][j];}}
}// 矩阵乘法
void matrixMultiply(int A[][3], int B[][2], int C[][2], int rows, int common, int cols) {for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {C[i][j] = 0;for (int k = 0; k < common; k++) {C[i][j] += A[i][k] * B[k][j];}}}
}int main() {int A[2][3] = {{1, 2, 3}, {4, 5, 6}};int B[3][2] = {{7, 8}, {9, 10}, {11, 12}};int C[2][2] = {0};matrixMultiply(A, B, C, 2, 3, 2);// 输出结果for (int i = 0; i < 2; i++) {for (int j = 0; j < 2; j++) {printf("%d ", C[i][j]);}printf("\n");}return 0;
}
游戏开发
二维数组常用于表示游戏地图、棋盘等:
#include <stdio.h>// 定义棋盘大小
#define BOARD_SIZE 3// 打印井字棋棋盘
void printBoard(char board[BOARD_SIZE][BOARD_SIZE]) {for (int i = 0; i < BOARD_SIZE; i++) {for (int j = 0; j < BOARD_SIZE; j++) {printf(" %c ", board[i][j]);if (j < BOARD_SIZE - 1) printf("|");}printf("\n");if (i < BOARD_SIZE - 1) {for (int j = 0; j < BOARD_SIZE; j++) {printf("---");if (j < BOARD_SIZE - 1) printf("+");}printf("\n");}}
}int main() {// 初始化棋盘char board[BOARD_SIZE][BOARD_SIZE] = {{' ', ' ', ' '},{' ', ' ', ' '},{' ', ' ', ' '}};// 放置棋子board[0][0] = 'X';board[1][1] = 'O';board[2][2] = 'X';// 打印棋盘printBoard(board);return 0;
}
图像处理
二维数组可用于存储和处理图像数据:
#include <stdio.h>// 定义图像大小
#define WIDTH 4
#define HEIGHT 3// 图像灰度反转
void invertImage(unsigned char image[HEIGHT][WIDTH]) {for (int i = 0; i < HEIGHT; i++) {for (int j = 0; j < WIDTH; j++) {image[i][j] = 255 - image[i][j]; // 灰度反转}}
}int main() {// 示例灰度图像(0-255)unsigned char image[HEIGHT][WIDTH] = {{100, 150, 200, 250},{50, 100, 150, 200},{0, 50, 100, 150}};// 打印原始图像printf("原始图像:\n");for (int i = 0; i < HEIGHT; i++) {for (int j = 0; j < WIDTH; j++) {printf("%3d ", image[i][j]);}printf("\n");}// 图像反转invertImage(image);// 打印处理后的图像printf("\n反转后的图像:\n");for (int i = 0; i < HEIGHT; i++) {for (int j = 0; j < WIDTH; j++) {printf("%3d ", image[i][j]);}printf("\n");}return 0;
}
注意事项
-
内存越界风险:C语言不检查数组越界,访问越界可能导致程序崩溃或数据损坏。
-
多维数组大小限制:大型多维数组可能导致栈溢出,建议使用动态内存分配。
-
初始化规则:部分初始化时,未指定的元素自动初始化为0。
-
指针与数组的区别:虽然二维数组名可视为指针,但它本身不是指针,不能进行自增/自减操作。
-
行优先存储:C语言中二维数组按行优先存储,这会影响缓存命中率和遍历效率。
总结
二维数组是C语言中处理表格、矩阵等结构化数据的强大工具。掌握其使用方法需要理解:
- 内存布局:按行优先连续存储
- 初始化规则:完全初始化、部分初始化和自动推断行数
- 访问方式:下标访问和指针访问
- 函数参数传递:必须指定列数
- 动态内存分配:指针数组或连续内存块
合理使用二维数组可以高效解决矩阵运算、游戏开发、图像处理等领域的问题。但需注意内存管理和边界检查,避免潜在的程序错误。