动态数组
** p 相当于一个指向指针的指针
比如我去创建一个 int 数组,用
#include <stdio.h>
int main() {
int *p=(int *)malloc(4*sizeof(int));
for(int i=0;i<4;i++) p[i]=i+1;
for(int i=0;i<4;i++) printf("%d ",p[i]);
return 0;
}
那我开一个都是指针的数组,比如说一个行指针数组,里面存放着二维数组的行指针。那么,此时,指向指针的指针,相当于把数组元素换成 int * 了,那么你自然的指向这个数组就要多带一个 *。
#include <stdio.h>
int main() {
int **p=(int **)malloc(4*sizeof(int*));
int tot=1;
for(int i=0;i<4;i++) {
p[i]=(int *)malloc(4*sizeof(int)); //p[i] 为一个指针,指向的应该是这一行第一个元素的地址
for(int j=0;j<4;j++) p[i][j]=++tot;
}
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) printf("%d ",p[i][j]);
printf("\n");
}
return 0;
}
动态二维数组作为参数详解
一、为什么需要动态二维数组?
在讲解具体用法前,我们先理解为什么需要动态二维数组。静态数组在编译时就必须确定大小,但很多实际场景中,数组大小需要在运行时才能确定:
// 静态数组 - 大小固定
int static_matrix[10][20]; // 只能处理10×20的矩阵
// 但实际需求可能是:
int rows, cols;
printf("请输入矩阵的行数和列数: ");
scanf("%d %d", &rows, &cols); // 用户输入决定大小
这时候就需要动态二维数组来解决这个问题。
二、动态二维数组的内存模型
理解动态二维数组的关键是要明白它的内存布局与静态二维数组不同:
静态二维数组(连续存储)
┌───┬───┬───┬───┬───┬───┐
│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ ← 所有元素连续存储
└───┴───┴───┴───┴───┴───┘
matrix[0] matrix[1]
动态二维数组(非连续存储)
┌───────┐ ┌───┬───┬───┐
│ 地址1 │───→│ 1 │ 2 │ 3 │ ← 第0行
└───────┘ └───┴───┴───┘
┌───────┐ ┌───┬───┬───┐
│ 地址2 │───→│ 4 │ 5 │ 6 │ ← 第1行
└───────┘ └───┴───┴───┘
┌───────┐ ┌───┬───┬───┐
│ 地址3 │───→│ 7 │ 8 │ 9 │ ← 第2行
└───────┘ └───┴───┴───┘
关键区别:动态二维数组的每一行都是独立分配的内存块,可能不连续!
三、创建动态二维数组的完整流程
3.1 分步创建过程
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows, cols;
// 步骤1:获取矩阵大小
printf("请输入矩阵的行数和列数: ");
scanf("%d %d", &rows, &cols);
// 步骤2:创建"行指针数组"
int **matrix = (int**)malloc(rows * sizeof(int*));
if (matrix == NULL) {
printf("内存分配失败!\n");
return -1;
}
// 步骤3:为每一行分配内存
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
printf("第%d行内存分配失败!\n", i);
// 需要释放之前已分配的内存
for (int j = 0; j < i; j++) {
free(matrix[j]);
}
free(matrix);
return -1;
}
}
// 步骤4:使用矩阵
printf("动态二维数组创建成功!大小: %d×%d\n", rows, cols);
// 步骤5:释放内存(重要!)
for (int i = 0; i < rows; i++) {
free(matrix[i]); // 先释放每一行
}
free(matrix); // 再释放行指针数组
return 0;
}
四、动态二维数组作为函数参数
4.1 函数声明和定义
由于动态二维数组实际上是"指针的指针",所以函数参数应该使用 int ** 类型:
#include <stdio.h>
#include <stdlib.h>
// 函数声明:动态二维数组作为参数
void initialize_matrix(int **matrix, int rows, int cols);
void print_matrix(int **matrix, int rows, int cols);
void free_matrix(int **matrix, int rows);
// 创建动态二维数组的函数
int** create_matrix(int rows, int cols) {
int **matrix = (int**)malloc(rows * sizeof(int*));
if (matrix == NULL) return NULL;
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
// 分配失败,清理已分配的内存
for (int j = 0; j < i; j++) {
free(matrix[j]);
}
free(matrix);
return NULL;
}
}
return matrix;
}
// 初始化矩阵
void initialize_matrix(int **matrix, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j + 1; // 填充数据
}
}
}
// 打印矩阵
void print_matrix(int **matrix, int rows, int cols) {
printf("矩阵内容:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%4d", matrix[i][j]);
}
printf("\n");
}
}
// 释放矩阵内存
void free_matrix(int **matrix, int rows) {
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
}
int main() {
int rows = 3, cols = 4;
// 创建动态二维数组
int **my_matrix = create_matrix(rows, cols);
if (my_matrix == NULL) {
printf("创建矩阵失败!\n");
return -1;
}
// 使用函数操作矩阵
initialize_matrix(my_matrix, rows, cols);
print_matrix(my_matrix, rows, cols);
// 释放内存
free_matrix(my_matrix, rows);
return 0;
}
五、理解 int **matrix 的含义
这是最让人困惑的部分,我们来拆解理解:
int **matrix; // 这是什么?
// 分解理解:
// 1. matrix 是一个指针,指向什么?
// 2. 它指向的是 int* 类型(整数指针)
// 3. 所以 matrix 是"指向整数指针的指针"
// 实际内存布局:
matrix → [指针0] → [整数00, 整数01, 整数02...] // 第0行
[指针1] → [整数10, 整数11, 整数12...] // 第1行
[指针2] → [整数20, 整数21, 整数22...] // 第2行
访问元素的各种等价写法:
matrix[i][j] // 最直观的写法
*(matrix[i] + j) // 先取第i行指针,再偏移j个元素
*(*(matrix + i) + j) // 完全用指针运算
六、完整实战示例:矩阵运算
#include <stdio.h>
#include <stdlib.h>
// 矩阵相加:C = A + B
int matrix_add(int **A, int **B, int **C, 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];
}
}
return 0;
}
// 矩阵转置
int matrix_transpose(int **src, int **dst, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
dst[j][i] = src[i][j]; // 行列互换
}
}
return 0;
}
int main() {
int rows = 2, cols = 3;
// 创建三个矩阵
int **A = create_matrix(rows, cols);
int **B = create_matrix(rows, cols);
int **C = create_matrix(rows, cols);
int **T = create_matrix(cols, rows); // 转置矩阵大小互换
// 初始化A, B
initialize_matrix(A, rows, cols);
initialize_matrix(B, rows, cols);
// 矩阵相加
matrix_add(A, B, C, rows, cols);
printf("矩阵A:\n");
print_matrix(A, rows, cols);
printf("矩阵B:\n");
print_matrix(B, rows, cols);
printf("矩阵C = A + B:\n");
print_matrix(C, rows, cols);
// 矩阵转置
matrix_transpose(A, T, rows, cols);
printf("A的转置矩阵:\n");
print_matrix(T, cols, rows);
// 释放所有内存
free_matrix(A, rows);
free_matrix(B, rows);
free_matrix(C, rows);
free_matrix(T, cols);
return 0;
}
七、常见错误与调试技巧
7.1 内存泄漏检测
// 错误示例:只释放了部分内存
void wrong_free(int **matrix, int rows) {
free(matrix); // 只释放了行指针数组,没释放每一行!
// 应该先释放所有 matrix[i],再释放 matrix
}
// 正确做法:按分配顺序逆序释放
void correct_free(int **matrix, int rows) {
for (int i = 0; i < rows; i++) {
free(matrix[i]); // 释放每一行
}
free(matrix); // 释放行指针数组
}
7.2 边界检查
void safe_access(int **matrix, int rows, int cols, int i, int j) {
if (i < 0 || i >= rows || j < 0 || j >= cols) {
printf("索引越界: (%d, %d)\n", i, j);
return;
}
printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]);
}
八、总结
动态二维数组作为参数的核心要点:
- 理解
int **类型:这是"指针的指针",指向一个指针数组 - 内存非连续:每一行独立分配,可能不连续存储
- 必须传递行列参数:函数内无法自动获取大小信息
- 严格的内存管理:分配时检查返回值,释放时按正确顺序
- 灵活的尺寸:可以创建不规则二维数组(每行长度不同)
通过这种理解,你就能熟练使用动态二维数组来解决各种复杂的编程问题了!

浙公网安备 33010602011771号