二维数组的定义
静态定义
语法:数据类型 数组名[行数][列数];
举个栗子:
int matrix[3][4];
特点:在编译时分配内存,行数和列数必须是常量表达式。
动态定义
语法:使用指针的指针(例如,int**matrix;)结合malloc或calloc函数。
举个栗子:
#include <>
#include <>
int main() {
int rows = 3; // 定义行数
int cols = 4; // 定义列数
int **matrix; // 声明一个指向指针的指针
// 动态分配行指针数组
matrix = (int **)malloc(rows * sizeof(int *));
if (matrix == NULL) {
fprintf(stderr, "Memory allocation failed!\n");
return 1;
}
// 动态分配每一行的内存
for (int i = 0; i < rows; ++i) {
matrix[i] = (int *)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
fprintf(stderr, "Memory allocation failed for row %d!\n", i);
// 释放之前分配的内存
for (int j = 0; j < i; ++j) {
free(matrix[j]);
}
free(matrix);
return 1;
}
}
// 赋值
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
matrix[i][j] = i * cols + j + 1; // 示例赋值
}
}
// 打印二维数组
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 释放内存
for (int i = 0; i < rows; ++i) {
free(matrix[i]); // 释放每一行的内存
}
free(matrix); // 释放行指针数组的内存
return 0;
}
特点:在运行时分配内存,适用于行数或列数不确定的情况。
二维数组的属性
行数和列数
行数:数组的第一个维度,表示数组有多少行。
列数:数组的第二个维度,表示数组有多少列。
元素类型
可以是任何基本数据类型(如int、float、char等)或复合数据类型(如结构体)。
二维数组的初始化
静态初始化
在数组定义时直接赋值。
举个栗子
int matrix[2][3]={{1,2,3},{4,5,6}};
动态初始化
通过循环或其他逻辑在运行时赋值。
举个栗子
for(int i=0; i<rows; ++i) { // 开始一个循环,循环变量i从0开始,直到小于rows(行数),每次循环i递增1。
// 这个外层循环用于遍历每一行
for(int j=0; j<cols; ++j) { // 在内层开始另一个循环,循环变量j从0开始,直到小于cols(列数),每次循环j递增1。
// 这个内层循环用于遍历当前行的每一列
matrix[i][j] = i*cols + j; // 将当前行的当前列的元素设置为i*cols + j,这里是一个简单的计算,将行索引与列索引结合来赋值
// 实际上,这行代码为二维数组赋值,创建了一个递增的序列
} // 内层循环结束
// 完成当前行的所有列的赋值
} // 外层循环结束
// 完成所有行的赋值
二维数组的访问
访问单个元素
使用行索引和列索引访问。
举个栗子
#include <>
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int matrix[3][4] = { // 静态初始化二维数组
{1, 2, 3, 4}, // 第0行
{5, 6, 7, 8}, // 第1行
{9, 10, 11, 12} // 第2行
};
// 定义要访问的元素的行索引和列索引
int targetRow = 1; // 我们想要访问第1行(注意:索引从0开始)
int targetCol = 2; // 我们想要访问第1行的第3列(索引从0开始)
// 访问并打印指定位置的元素
printf("Element at matrix[%d][%d] is: %d\n", targetRow, targetCol, matrix[targetRow][targetCol]);
return 0;
}
遍历整个数组
使用嵌套循环,外层循环控制行,内层循环控制列。
举个栗子
创建一个3行4列的二维数组,并为其每个元素赋值。
for(int i=0; i<rows; ++i) { // 开始一个循环,循环变量i从0开始,直到小于rows(行数),每次循环i递增1。
// 这个外层循环用于遍历二维数组的每一行
for(int j=0; j<cols; ++j) { // 在内层开始另一个循环,循环变量j从0开始,直到小于cols(列数),每次循环j递增1。
// 这个内层循环用于遍历当前行的每一列
// 访问或操作matrix[i][j] // 这里是一个占位符,表示在这个位置可以进行对matrix[i][j]的访问或操作。
// 例如,可以打印matrix[i][j]的值,或者对它进行赋值,或者进行其他操作。
} // 内层循环结束
// 完成当前行的所有列的访问或操作
} // 外层循环结束
// 完成所有行的访问或操作
二维数组的操作
赋值
为数组元素赋予新的值,可以是直接赋值或通过计算。
举个栗子
创建一个3行4列的二维数组,并为其每个元素赋值。
#include <>
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int matrix[3][4]; // 定义一个3行4列的二维数组
// 为二维数组的每个元素赋值
for(int i=0; i<rows; ++i) { // 外层循环遍历行
for(int j=0; j<cols; ++j) { // 内层循环遍历列
// 赋值操作
matrix[i][j] = i * cols + j; // 赋值逻辑,这里只是示例,可以根据需要修改
}
}
// 打印二维数组以验证赋值是否成功
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
printf("%d ", matrix[i][j]); // 打印矩阵元素
}
printf("\n"); // 每行结束后换行
}
return 0;
}
遍历
按照行优先或列优先的顺序访问数组元素。
举个栗子
创建一个3行4列的二维数组,并遍历其每个元素,打印出它们的值。
#include <>
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int matrix[3][4]; // 定义一个3行4列的二维数组
// 初始化二维数组(这里只是打印出初始值,没有实际赋值)
for(int i=0; i<rows; ++i) { // 外层循环遍历行
for(int j=0; j<cols; ++j) { // 内层循环遍历列
printf("%d ", matrix[i][j]); // 打印矩阵元素
}
printf("\n"); // 每行结束后换行
}
return 0;
}
输出
使用 printf 函数将数组内容打印到控制台。
举个栗子
创建一个3行4列的二维数组,并遍历其每个元素,打印出它们的值。
#include <>
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int matrix[3][4]; // 定义一个3行4列的二维数组
// 初始化二维数组
for(int i=0; i<rows; ++i) { // 外层循环遍历行
for(int j=0; j<cols; ++j) { // 内层循环遍历列
matrix[i][j] = i * cols + j; // 赋值逻辑,这里只是示例,可以根据需要修改
}
}
// 打印二维数组以验证赋值是否成功
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
printf("%d ", matrix[i][j]); // 打印矩阵元素
}
printf("\n"); // 每行结束后换行
}
return 0;
}
转置
交换数组的行和列,适用于矩阵操作。
需要创建一个新的数组来存储转置后的结果。
举个栗子
如何将一个3行4列的二维数组转置。
#include <>
// 函数声明
void transposeMatrix(int **matrix, int **transposed, int rows, int cols);
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int matrix[3][4]; // 定义一个3行4列的二维数组
int transposed[4][3]; // 转置后的二维数组,需要提前声明
// 初始化二维数组
for(int i=0; i<rows; ++i) { // 外层循环遍历行
for(int j=0; j<cols; ++j) { // 内层循环遍历列
matrix[i][j] = i * cols + j; // 赋值逻辑,这里只是示例,可以根据需要修改
}
}
// 转置二维数组
transposeMatrix(matrix, transposed, rows, cols);
// 打印原始二维数组
printf("原始二维数组:\n");
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 打印转置后的二维数组
printf("转置后的二维数组:\n");
for(int i=0; i<cols; ++i) {
for(int j=0; j<rows; ++j) {
printf("%d ", transposed[i][j]);
}
printf("\n");
}
return 0;
}
// 函数定义
void transposeMatrix(int **matrix, int **transposed, int rows, int cols) {
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
transposed[j][i] = matrix[i][j]; // 交换行和列
}
}
}
二维数组的内存管理
静态分配
内存分配在栈上,程序结束时自动释放。
静态分配是指在编译时就已经知道数组的大小,因此可以在栈上分配内存。
这种方法适用于数组大小固定且在程序执行过程中不需要改变的情况。
举个栗子
在这个例子中,我们定义了一个3行4列的二维数组matrix
,并在栈上分配了内存。
数组的大小在编译时就已经确定,因此不需要在运行时进行内存分配或释放。
#include <>
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int matrix[3][4]; // 静态分配二维数组
// 初始化二维数组
for(int i=0; i<rows; ++i) { // 外层循环遍历行
for(int j=0; j<cols; ++j) { // 内层循环遍历列
matrix[i][j] = i * cols + j; // 赋值逻辑,这里只是示例,可以根据需要修改
}
}
// 打印二维数组以验证赋值是否成功
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
printf("%d ", matrix[i][j]); // 打印矩阵元素
}
printf("\n"); // 每行结束后换行
}
return 0;
}
动态分配
使用malloc或calloc在堆上分配内存。
需要手动释放内存,以避免内存泄漏。
动态分配是指在运行时使用malloc
或calloc
函数在堆上分配内存。
这种方法适用于数组大小在运行时才确定的情况,或者需要频繁更改数组大小的情况。
举个栗子
在这个例子中,我们首先声明了一个指向指针的指针matrix。
然后在运行时使用malloc
为行指针数组分配内存。
#include <>
#include <>
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int **matrix; // 声明一个指向指针的指针
// 动态分配行指针数组
matrix = (int **)malloc(rows * sizeof(int *));
if (matrix == NULL) {
fprintf(stderr, "Memory allocation failed!\n");
return 1;
}
// 动态分配每一行的内存
for (int i = 0; i < rows; ++i) {
matrix[i] = (int *)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
fprintf(stderr, "Memory allocation failed for row %d!\n", i);
// 释放之前分配的内存
for (int j = 0; j < i; ++j) {
free(matrix[j]);
}
free(matrix);
return 1;
}
}
// 初始化二维数组
for(int i=0; i<rows; ++i) { // 外层循环遍历行
for(int j=0; j<cols; ++j) { // 内层循环遍历列
matrix[i][j] = i * cols + j; // 赋值逻辑,这里只是示例,可以根据需要修改
}
}
// 打印二维数组以验证赋值是否成功
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
printf("%d ", matrix[i][j]); // 打印矩阵元素
}
printf("\n"); // 每行结束后换行
}
// 释放内存
for (int i = 0; i < rows; ++i) {
free(matrix[i]); // 释放每一行的内存
}
free(matrix); // 释放行指针数组的内存
return 0;
}
二维数组与函数
作为参数传递
传递数组时,必须指定除第一维外的所有维度。
举个栗子
当二维数组作为函数的参数传递时,必须指定除第一维外的所有维度。
这意味着我们需要告诉函数数组的行数和列数。
在这个例子中,printMatrix
函数接受一个二维数组matrix
以及它的行数rows
和列数cols
。通过这些参数,函数知道如何遍历和打印数组。
#include <>
// 函数声明
void printMatrix(int rows, int cols, int matrix[rows][cols]);
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int matrix[3][4]; // 定义一个3行4列的二维数组
// 初始化二维数组
for(int i=0; i<rows; ++i) { // 外层循环遍历行
for(int j=0; j<cols; ++j) { // 内层循环遍历列
matrix[i][j] = i * cols + j; // 赋值逻辑,这里只是示例,可以根据需要修改
}
}
// 调用函数打印二维数组
printMatrix(rows, cols, matrix);
return 0;
}
// 函数定义
void printMatrix(int rows, int cols, int matrix[rows][cols]) {
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
printf("%d ", matrix[i][j]); // 打印矩阵元素
}
printf("\n"); // 每行结束后换行
}
}
作为返回值
不能直接返回局部数组,但可以返回指向动态分配数组的指针。
二维数组不能直接作为函数的返回值,因为函数的返回值是返回给调用者的。
而二维数组在函数内部创建,无法直接返回给调用者。
但是,你可以返回指向动态分配数组的指针。
举个栗子
#include <>
#include <>
// 函数声明
int **createMatrix(int rows, int cols);
int main() {
int rows = 3; // 定义二维数组的行数
int cols = 4; // 定义二维数组的列数
int **matrix = createMatrix(rows, cols); // 调用函数创建二维数组
// 打印二维数组以验证创建是否成功
for(int i=0; i<rows; ++i) {
for(int j=0; j<cols; ++j) {
printf("%d ", matrix[i][j]); // 打印矩阵元素
}
printf("\n"); // 每行结束后换行
}
// 释放内存
for (int i = 0; i < rows; ++i) {
free(matrix[i]); // 释放每一行的内存
}
free(matrix); // 释放行指针数组的内存
return 0;
}
// 函数定义
int **createMatrix(int rows, int cols) {
int **matrix = (int **)malloc(rows * sizeof(int *));
if (matrix == NULL) {
fprintf(stderr, "Memory allocation failed!\n");
return NULL;
}
// 动态分配每一行的内存
for (int i = 0; i < rows; ++i) {
matrix[i] = (int *)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
fprintf(stderr, "Memory allocation failed for row %d!\n", i);
// 释放之前分配的内存
for (int j = 0; j < i; ++j) {
free(matrix[j]);
}
free(matrix);
return NULL;
}
}
// 返回指向动态分配数组的指针
return matrix;
}
二维数组的边界检查
确保索引在数组的合法范围内,避免越界访问。
二维数组的性能考虑
访问局部性
连续访问相邻元素可以优化缓存性能。
内存对齐
某些数据类型可能需要按照特定的对齐方式来存储,以提高访问效率。