有了malloc函数的基本素养,就能够开辟一个一维动态数组。
而一个二维数组需要如何开辟呢?好像之前的malloc素养不够用了。
?我们这里以开辟一个四行四列的二维数组为例。
指针数组开辟
先是开辟语句:?
int** p = (int**)malloc(sizeof(int*) * 4);
for (int i = 0; i < 4; i++)
{
p[i] = (int*)malloc(sizeof(int)*4);
}
首先我们开辟了内含四个指针的指针数组:
随后我们通过一个循环依次开辟他们的每个地址后所跟随的长度为四的整型数组:
这样也就使得我们能够通过每一个指针数组里的地址访问到相应的行列,每一个地址可以找到对应的行开头,通过行开头能够找都后续的列元素,而与之对应的长度为四的整型数组就可以存储该行所有的列元素,一个二维动态数组也就这样建好啦!
直接用指针数组来进行开辟十分符合我们的思维习惯,所以非常好理解,但是他也同样具备释放较为麻烦,并且无法保证一行最后一个元素与下一行第一个元素是连续存储的问题。
用指针数组开辟二维数组,一行最后一个元素与下一行第一个元素并不是连续存储:
且具备内存较难释放的问题:
我们需要先释放存储列元素的数组,然后释放我们之前所开辟的行指针数组。
//先释放每行的元素
for(int i=0; i<4; i++)
{
free(p[i]);
}
//最后释放二级指针
free(p);
?数组指针开辟
数组指针的开辟方法就解决了指针数组开辟释放难、行最后与下一行最前元素不连续的问题。
const int ROW = 4;
const int COL = 4;//数组指针列数可以用常量定义
int(*pp)[COL] = (int(*)[COL])malloc(ROW * COL * sizeof(int));
?我们告诉了编译器该数组的列数为COL,接下来在开辟过程他会为我们做如下事情。
当编译器检测到我们列元素达到COL时,他会知道需要开始下一列了,于是将我们的十六个元素划分为了四个行?。
由于我们数组一次动态分配完毕,那自然我们一行的最后一个元素也就与下一行的第一个元素对应上了。
整个释放过程也变得及其简单,直接一个free(p)就完了,确实比原先的指针数组开辟简化不少。
但我们仍然需要注意到:他的列数是需要给出固定值的,这也是他的缺陷所在。
一维数组模拟开辟
?其实这便是用二维数组的思维来管理一维数组,与其他的一维数组具体差异在于赋值与打印方面
int* ppp = (int*)malloc(ROW*COL*sizeof(int));
//赋值
for(int i=0; i<ROW ;i++)
for (int j = 0; j < COL; j++)
{
ppp[i * ROW + j] = i * ROW + j;
}
//打印
printf("一维数组模拟二维数组\n");
for (int i= 0; i < ROW; i++)
for (int j = 0; j < COL; j++)
{
printf("%-4d ", ppp[i * ROW + j]);
if (j == COL - 1)
printf("\n");
}
free(ppp);
通过 i*ROW+j 的索引来映射他在二维数组中的位置
实验代码(供以调试)?
#include<stdio.h>
#include<stdlib.h>
//动态内存申请二维数组
//1.指针数组
//每一行元素地址连续 但是不能保证上一行的尾和下一行的头挨着
//int main()
//{
// int** p = (int **)malloc(3*sizeof(int*));//竖的那个数组申请好了
//
// for(int i=0; i<3; i++)
// {
// p[i] = (int*)malloc(4*sizeof(int));
// }
//
// for(int i=0; i<3; i++)
// {
// for(int j=0; j<4; j++)
// {
// printf("%p\n", &p[i][j]);
// }
// }
// for(int i=0; i<3; i++)
// {
// free(p[i]);
// }
// free(p);
// return 0;
//}
//2.用数组指针
//int main()
//{
// int (*p)[4] = (int(*)[4])malloc(3*4*sizeof(int));
//
// for(int i=0; i<3; i++)
// {
// for(int j=0; j<4; j++)
// {
// printf("%p\n", &p[i][j]);
// }
// }
// free(p);
//
// return 0;
//}
//3.用二维数组的思想去管理申请的一维数组
//int main()
//{
// int *p = (int*)malloc(3*4*sizeof(int));
//
// for(int i=0; i<3; i++)
// {
// for(int j=0; j<4; j++)
// {
// p[i*4+j] = 1;
// printf("%p\n", &p[i*4+j]);
// }
// }
//
// free(p);
//
// return 0;
//}
int main()
{
//动态开辟二维数组
//1 指针数组开辟
// 释放较为麻烦,并且无法保证一行最后一个元素与下一行第一个元素是连续存储的
// int** p = (int **)malloc(3*sizeof(int*));//竖的那个数组申请好了
//
// for(int i=0; i<3; i++)
// {
// p[i] = (int*)malloc(4*sizeof(int));
// }
int** p = (int**)malloc(sizeof(int*) * 4);
for (int i = 0; i < 4; i++)
{
p[i] = (int*)malloc(sizeof(int)*4);
}
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
p[i][j] = j;
}
}
printf("指针数组开辟二维数组\n");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
printf("%d ",p[i][j]);
if (j == 3)
printf("\n");
}
}
//先释放每行的元素
for(int i=0; i<4; i++)
{
free(p[i]);
}
//最后释放二级指针
free(p);
//2. 数组指针开辟二维数组
// int (*p)[4] = (int(*)[4])malloc(3*4*sizeof(int));
//
// for(int i=0; i<3; i++)
// {
// for(int j=0; j<4; j++)
// {
// printf("%p\n", &p[i][j]);
// }
// }
//释放简单,一行最后一个元素与下一行第一个元素连续存储,但列数需固定,无法自定义列数
const int ROW = 4;
const int COL = 4;//数组指针列数可以用常量定义
printf("数组指针开辟二维数组,打印地址\n");
int(*pp)[COL] = (int(*)[COL])malloc(ROW * COL * sizeof(int));
for(int i=0; i<4; i++)
{
for(int j=0; j<4; j++)
{
printf("%p\n", &pp[i][j]);
}
}
free(pp);
//3. 一维数组当作二维数组来管理
//可使用变量来作为行数和列数,释放简单,但脑海里需要绕弯子易出错,赋值与打印十分麻烦
int* ppp = (int*)malloc(ROW*COL*sizeof(int));
//赋值
for(int i=0; i<ROW ;i++)
for (int j = 0; j < COL; j++)
{
ppp[i * ROW + j] = i * ROW + j;
}
//打印
printf("一维数组模拟二维数组\n");
for (int i= 0; i < ROW; i++)
for (int j = 0; j < COL; j++)
{
printf("%-4d ", ppp[i * ROW + j]);
if (j == COL - 1)
printf("\n");
}
free(ppp);
}
?实验结果:
(如有问题,欢迎指正)?
|