方法一:内存不连续
假设数组元素的数据类型是int型,则动态分配二维数组的一般方法是这样:
int **p = NULL;
p = (int **)malloc(nWidth * sizeof(int *));
if (!p)
return NULL;
for (int j = 0; j < nWidth; j++)
{
p[j] = (int*)malloc(nHeight * sizeof(int))
if (!p[j])
return NULL;
}
这段代码浅显易懂,先分配第1维,在循环分配第2维。假设二维数组是3×2的,每一句运行完后的内存情况如图所示(方格表示内存,xx表示随机数,方格下方是内存地址。当然,地址只是示意而已,与真实情况并不相符)
第一句完后分配了3个内存单元
循环分配后,注意下面3段内存通常是不连续的。这样用下表p[n][m]操作数组没问题,如果整块内存操作就会有问题了,比如下面这句:
原意是想把下面的3块6个内存单元清0,可是事与愿违,把从p开始后面6个内存单元清0了,p[]不能用了。p后面只有3个已分配的内存单元,却要操作6个,另外3个是未知区域。清了后面虚线的3块未知区域,这就很危险了,可能导致程序崩溃。
这样分配的内存需要循环释放。代码如下:
for (int j = 0; j < nWidth; j++)
{
free(p[j]);
p[j] = NULL;
}
free(p);
p = NULL;
方法二:内存连续
若要动态分配内存连续的二维数组,那么可以采用如下方法:
//p是一个指向指针的指针
int **p = NULL;
//请求系统分配一个包含nWidth个int*指针的空间,p指向空间的首地址。
//此空间用来作为二维数组的索引
p = (int **)malloc(nWidth * sizeof(int *));
if (!p)//若malloc失败
return NULL;
//请求系统分配一个包含nWidth*nHeight个int指针的空间,p[0]指向空间的首地址。
//此空间用来保存二维数组的数据
p[0] = (int *)malloc(nHeight * nWidth * sizeof(int));
if (!p[0])//若malloc失败
{
free(p);
return NULL;
}
//索引与相应数据的关联
for(int i = 1; i < nWidth; i++)
p[i] = p[i-1] + nHeight;//留意并思考此处为什么是直接加上nHeight的值
memset(p[0], 0, nHeight * nWidth);//二维数组清零
这段代码解决了分配的空间不连续的问题。每一句运行完后的内存情况如图所示:
第一句和上面一样。
这6个内存单元是一次分配的,所以连续。
这个二维数组的数据首地址是p[0],p是第2维的索引首地址。所以如果要对二维数组进行整体的内存(缓冲区 buffer)操作,要以p[0]为操作对象的首地址。
到此,索引与对应的数据地址关联上了。这个二维数组既可以通过下表p[][]来操作,又可以操作缓冲区。操作缓冲区的函数比如memcpy,cfile的writehuge和readhuge使用起来很方便,省去了2次循环的麻烦。
至于释放,不必循环释放。因为malloc了2次,所以只需free两次就行了:
free(p[0]); //释放二维数组
p[0] = NULL;
free(p); //释放指针空间
p = NULL;