Многомерные массивы, выделенные через calloc

У меня вопрос о том, как выделяется память при я 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]

Тип 3:

double  **array1;
int ii;

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

Из того, что я понимаю в calloc и malloc, разница между двумя последними состоит в том, что calloc будет обнулять все элементы массива, тогда как malloc не будет. Но являются ли первые два способа определения эквивалентного массива в памяти?

Ответ 1

Являются ли первые два способа определения эквивалентного массива в памяти?

Не совсем. Во втором типе они почти наверняка смежны, в то время как в первом типе это не обязательно.

Тип 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| | |
          +---+

Ответ 2

Простой пример

#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;
}

Ответ 3

В первом случае вы выделяете 10 указателей, чтобы удвоить, и 100 двойных. Во втором случае вы выделяете 100 указателей, чтобы удвоить. Другое отличие состоит в том, что во втором случае вы выделяете один большой блок памяти, так что все элементы вашего массива находятся в одном блоке. В первом случае каждая "строка" вашего массива находится в другом блоке, чем остальные. Хотя, во-вторых, ваш массив должен быть двойным *, а не двойным **, потому что в этом случае выделения ваш массив содержит только указатели, чтобы удвоить, а не удваивать.

Ответ 4

В случае 1 вы делаете:

array1[0] -> [memory area of 10]
array1[1] -> [memory area of 10] ...
array1[N] -> [memory area of 10] ...

Примечание. Вы не можете предположить, что область памяти непрерывна, могут быть пробелы.

В случае 2 вы делаете:

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.