Многомерные массивы, выделенные через 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.