通过calloc分配的多维数组

14

我有一个关于calloc的内存分配问题。我看了这个问题,但它没有解释在动态分配二维数组的情况下内存是如何分配的。

我想知道下面三种动态分配二维数组的方式之间是否有内存表示上的区别。

类型1:

double  **array1;
int ii;

array1 = calloc(10, sizeof(double *));
for(ii = 0; ii < 10; ii++) { 
   array1[ii] = calloc(10, sizeof(double));
}
// Then access array elements like array1[ii][jj]

类型 2:

double  **array1;
int ii;

array1 = calloc(10 * 10, sizeof(double *));
// Then access array elements like array1[ii + 10*jj]

第三种类型:

double  **array1;
int ii;

array1 = malloc(10 * 10, sizeof(double *));
// Then access array elements like array1[ii + 10*jj]
据我所知,callocmalloc的区别在于:calloc会将数组的所有元素都清零,而malloc则不会。但是,这两种定义数组的方式在内存中是否等效呢?

@JBL:实际上只是指针需要额外的空间,而且10个双精度块可能会变得不连续。 - Dancrumb
@Dancrumb 哦,确实,我没有想到。好观点。 - JBL
你链接的问题中的答案已经说明了一切。 - alk
1
可能是多维数组在内存中的格式化方式?的重复问题。 - alk
malloc不会分配一个二维数组,它只会分配一块原始的内存空间(一个一维字符数组)。你如何使用这块内存并不是malloc的责任。 - n. m.
显示剩余5条评论
4个回答

9

第一种和第二种定义数组的方式,在内存中等价吗?

不完全等价。在第二种类型中,它们几乎肯定是连续的,而在第一种类型中则不确定。

第1种: 内存表示会像这样:

          +---+---+---+---+---+---+---+---+---+---+
    double| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |   
          +---+---+---+---+---+---+---+---+---+---+ 
            ^
            |------------------------------------                                     
                .   .   .   .   .   .   .   .   |    // ten rows of doubles
                                                -
          +---+---+---+---+---+---+---+---+---+--|+
    double| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0||   
          +---+---+---+---+---+---+---+---+---+--|+
            ^   .   .   .                       -
            |   ^   ^   ^   .   .   .   .   .   |
            |   |   |   |   ^   ^   ^   ^   ^   |
          +-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+
array1[ii]| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | // each cell points to ten doubles
          +---+---+---+---+---+---+---+---+---+---+
            ^
            |
            |
          +-|-+
    array1| | |
          +---+

类型2:内存中的表示将如下所示:

          +---+---+---+---+---+---+---+---+---+---+     +---+
    double| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 |  
          +---+---+---+---+---+---+---+---+---+---+     +---+
            ^   ^   ^   ^   ^   ^   ^   ^   ^   ^         ^
            |   |   |   |   |   |   |   |   |   |         |
            |   |   |   |   |   |   |   |   |   |         |
          +-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+     +-|-+
array1[ii]| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... |99 | // each cell points to one double
          +---+---+---+---+---+---+---+---+---+---+     +---+
            ^
            |
            |
          +-|-+
    array1| | |
          +---+

1
简单示例
#include<stdio.h>
#include<stdlib.h>

int **d ;
int sum();

//----------------------------------------------  
int main(){

    d = (int **)calloc(3,sizeof(int*));
    printf("\n%d",sum());     
}

//-----------------------------------------------
int sum(){
   int s = 0;
   for(int i = 0; i < 3; i++)
       d[i] = (int *) calloc (3,sizeof(int));

   for(int i = 0; i < 3; i++){ 
       for(int j = 0; j < 3; j++){
           d[i][j] = i+j;
           s += d[i][j];
           printf("\n array[%d][%d]-> %d",i,j,d[i][j]);
        }
   }
   return s;
}

0
在第一种情况下,您进行了以下操作:
array1[0] -> [memory area of 10]
array1[1] -> [memory area of 10] ...
array1[N] -> [memory area of 10] ...

注意:您不能假设内存区域是连续的,可能存在间隙。
在第二种情况下,您需要执行以下操作:
array1 -> [memory area of 100]

第3种情况与第2种情况相同,但它不初始化内存。第1种情况和第2种情况以及第3种情况的区别在于,在第一种情况下,您真正拥有2D内存结构。例如,如果您想交换第1行和第2行,您只需交换指针即可:
help      = array1[1] 
array1[1] = array1[2] 
array1[2] = help

但是如果你想在第2&3种情况下做同样的事情,你需要使用真正的memcpy。要使用什么?这取决于你正在做什么。

第一种方法使用了更多的内存:如果你有一个1000x10的数组,那么第一种版本将使用1000*8 + 1000*10*8(在64位系统上),而第2&3种方法只会使用1000*10*8。


第二和第三种情况使用的内存小于或等于第一种情况。 - alk
你说得完全正确。这就是我在最后一段用数字试图解释的内容,但是我打错了单词,应该是“第一”而不是“第二”。我已经修改了这篇文章。 - susundberg
你知道第二和第三种情况根本没有分配任何双精度浮点数,只是指向它们的指针吗? - alk
它们都分配一块内存(不是双倍内存或双倍*内存,只是内存!),并且在64位机器上sizeof(double*) == sizeof(double) == 8但在32位机器上,sizeof(double*) == 4,而sizeof(double) == 8。所以,是的,你是正确的,在示例代码的情况2和3中都是错误的,它们应该分配大小为double的内存元素,而不是double*。 - susundberg

0

第一种方式是你分配了10个指向double的指针和100个double。在第二种方式中,你分配了100个指向double的指针。另一个区别是,在第二种方式中,你分配了一个大的内存块,使得数组的所有元素都在同一个块中。而在第一种方式中,你的数组的每一行都在与其他行不同的块中。 然而,在第二种方式中,你的数组应该是一个double*而不是double**,因为在这种分配方式中,你的数组只包含指向double的指针,而不是double。


前两种方法不使用相同数量的内存。第一种方法使用指针本身大小的十倍以上。 - Dancrumb
第一种方法:10个指针,100个double。第二种方法:100个指针,没有double。是的,数量不同,我的错。但第二种方法仍然没有分配double。 - Waterfrag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接