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

У меня вопрос, как распределяется память, когда я calloc. Я просмотрел этот вопрос, но это не так. t адрес, как распределяется память в случае динамически распределяемого двумерного массива.

Мне было интересно, есть ли разница в представлении памяти между следующими тремя способами динамического выделения 2D-массива.

Тип 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 - нет. Но являются ли первые два способа определения массива эквивалентными в памяти?


person Kitchi    schedule 23.05.2013    source источник
comment
Тип 2 и Тип 3 в основном одно и то же, за исключением того, что для памяти будет установлено значение 0 с помощью calloc. Не знаю, есть ли реальная разница с Типом 1.   -  person JBL    schedule 23.05.2013
comment
@JBL: на самом деле просто дополнительное пространство для указателей и тот факт, что блоки из 10 двойников могут оказаться несмежными   -  person Dancrumb    schedule 23.05.2013
comment
@Dancrumb Да, конечно, я об этом не подумал. Хорошая точка зрения.   -  person JBL    schedule 23.05.2013
comment
Случаи 2 и 3 вообще не выделяют дублей.   -  person alk    schedule 23.05.2013
comment
Ответы на вопрос, который вы связали, говорят сами за себя.   -  person alk    schedule 23.05.2013
comment
возможный дубликат Как многомерные массивы форматируются в памяти?   -  person alk    schedule 23.05.2013
comment
@alk - Нет ... вопрос, который я связал, объясняет только, как хранятся двухмерные статические массивы. Здесь не говорится о динамически выделяемых массивах. Я бы сказал, что это дополнительный вопрос, а не дубликат.   -  person Kitchi    schedule 23.05.2013
comment
Схема одинакова для статического или динамического распределения, по крайней мере, если последнее использует правильное объявление переменной для выделения памяти, как в подходе с одним выстрелом: double (*doubles2d)[N][M] = malloc(N*M*sizeof(double));   -  person alk    schedule 23.05.2013
comment
malloc не выделяет 2D-массив. Он выделяет необработанный кусок памяти (одномерный массив символов). За то, что вы с ним сделаете, malloc не отвечает.   -  person n. 1.8e9-where's-my-share m.    schedule 15.10.2015
comment
Второй и третий примеры должны быть sizeof (double) вместо sizeof (double *), в третьем примере не должно быть запятой в параметрах malloc, и если вы используете форму [ii + 10 * jj]] ссылаясь, array1 должен иметь только тип double *   -  person JustinB    schedule 30.04.2019


Ответы (4)


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

Не совсем. Во втором типе они почти наверняка смежны, в то время как в первом типе нет уверенности.

Тип 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| | |
          +---+
person Ziezi    schedule 15.10.2015

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

#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;
}
person Monis Majeed    schedule 21.02.2014

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

person Marrow Gnawer    schedule 23.05.2013
comment
Первые два способа не используют одинаковый объем памяти. Первый метод использует в десять раз больше собственного размера указателя. - person Dancrumb; 23.05.2013
comment
Первый метод: 10 указателей, 100 двойных, второй метод: 100 указателей, без двойных, да, не то же самое, моя проблема. Но второй метод по-прежнему не имеет двойного выделения. - person Marrow Gnawer; 23.05.2013

В случае 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.

person susundberg    schedule 23.05.2013
comment
Случаи 2 и 3 используют меньше или равное количество памяти, чем первое. - person alk; 23.05.2013
comment
Вы абсолютно правы. Это то, что я пытался объяснить в последнем абзаце с числами, но имел "опечатку", говоря "второй", поскольку предполагалось, что это "первый". Я редактировал пост для этого. - person susundberg; 24.05.2013
comment
Вы знаете, что случаи 2 и 3 вообще не выделяют никаких двойников, а только указатели на них, не так ли? - person alk; 24.05.2013
comment
Ну, они оба выделяют кусок памяти (не двойную память или двойную * память, это просто память!), И с 64-битной машиной sizeof (double *) == sizeof (double) == 8, но на 32-битной машине sizeof (double *) = = 4, а sizeof (double) == 8. Так что да, вы правы, случаи 2 и 3 в примере кода неверны, они должны выделять элементы памяти размером double, а не double *. - person susundberg; 24.05.2013