多维数组的动态分配

来源:互联网 发布:欧洲工商管理学院 知乎 编辑:程序博客网 时间:2024/05/25 19:59

方法一:内存不连续

假设数组元素的数据类型是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;

    }

-------------------------------------------------------

C++版本

int **p=NULL; 
p=new int*[nWidth];
    if (!p){
        return NULL;
    }
    for (int j=0;j<nWidth;j++){
        p[j]=new int[nHeight];
        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*sizeof(int));//二维数组清零

这段代码解决了分配的空间不连续的问题。每一句运行完后的内存情况如图所示:

第一句和上面一样。

这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;

-------------------------------------------------------

C++ 版本

分配:

int **p=NULL; 
   p=new int *[nWidth];
if (!p){
        return NULL;
    }
    p[0]=new int[nWidth*nHeight];
if (!p[0]){
   delete[] p;
        return NULL;
    }
    ZeroMemory(p[0],nWidth*nHeight*sizeof(int));
    for (int i=1;i<nWidth;i++){
        p[i]=p[i-1]+nHeight;
    }


释放:

f(!p){
   return;
}
    delete []p[0];
    p[0]=NULL;
    delete[] p;
    p=NULL;

-------------------------------------------------------


C++中静态二维数组和动态二维数组的区别

这里所说的静态二维数组,就是在程序中类似int b[2][20]定义的数组;所说的动态二维数组,就是在程序中通过int **a = new int*[2];
a[0] = new int[20]; a[1] = new int[20]声明的数组。( 二维数组的本质还是一维数组。静态二维数组的存储空间等同于单一的一维数组,而动态二维数组等同于多个一维数组的组合,每一个一维数组,等同于二维数组的一个行向量。)

静态二维数组放在系统内存的stack里,动态数组放在内存的heap里面。这个却别不是本文讨论的重点。这里要着重说明的是,静态数组的存储空间是个地址连续的内存区域,而动态分配的二维数组空间,非常可能是不连续的地址空间。  


在Robert Sedgewick所著的Algorithm In C中,把二维数组当成数组的数组为它动态分配内存。

    首先,分配一个指针的数组,然后为每一行分配内存。
函数定义:
    void **malloc2d(int r, int c, int size)
    {
        int i;
        int **t = malloc(r * sizeof(void*));
        for(i=0; i<r; i++)
            t[i] = malloc(c * size);
        return t;
    }

    void free2d(void **arr, int r)
    {
        int i = 0;
        for(i=0; i<r; i++)

            free(arr[i]);
        free(arr);
    }

    这种方法好处是可以用a[i][j]的方式来访问数组元素,但是它需要分配额外内存来存储行指针。

    另外,这种方法分配的内存是不连续的,而且在分配和释放二维数组时,要多次调用malloc和free操作,增加了系统开销。而使用以下两种方法为二维数组分配的内存是连续的,可以避免因频繁分配和释放内存而带来的内存开销。

方法一:

    用一维数组来实现二维数组的功能,在内存中二维数组还是线性排列的。对于想申请a[m][n]数组可以如下实现:
    int   *ptr_array;
    ptr_array   =   (int*) malloc(m*n*sizeof(int));
当需要访问a[i][j]时,   用如下方式访问:  
  *(ptr_array   +   i   *   n   +   j);
当需要释放空间时,用如下方式访问:
    free ptr_array;

    这种方式实际上是把二维数组用一维数组表示,  再模拟成二维数组使用.

方法二:

void **malloc2d(int row, int col, int size)
{
    void **arr;
    arr = (void **) malloc(sizeof(void *) * row + size * row * col);
    if (arr != NULL)
    {
        void *head;
        head = (void *) arr + sizeof(void *) * row;
        memset(arr, 0, sizeof(void *) * row + size * row * col);
        while (row--)
            arr[row] = head + size * row * col;
    }
    return arr;
}

void free2d(void **arr)
{
    if (arr != NULL)
    free(arr);
}

    这种方法的好处和不足之处同Robert Sedgewick的方法一样,可以用a[i][j]的方式来访问,但仍然需要额外分配内存来存储行指针。但它分配的空间是连续的,可以避免因频繁分配和释放内存而带来的内存开销,这一点又可与方法一媲美。

最值得推荐的方法是方法二


参考资料:

http://hi.baidu.com/jiaon/item/52017c5a145debcfd2e10c52

http://hi.baidu.com/_forward001/blog/item/8a7a5f867095c63966096e59.html

http://topic.csdn.net/u/20081124/18/60e5259e-f1ae-4fe9-bda1-cd7b609250d8.html?seed=1045715561&r=60398843#r_60398843

http://hi.baidu.com/_forward001/blog/item/8a7a5f867095c63966096e59.html
http://topic.csdn.net/u/20081124/18/60e5259e-f1ae-4fe9-bda1-cd7b609250d8.html?seed=1045715561&r=60398843#r_60398843

http://blog.sina.com.cn/s/blog_700e74120100lmtd.html