Динамическое распределение массива объясняет
Это пример кода, который мой учитель показал нам: "Как динамически выделять массив в C?". Но я не совсем понимаю это. Вот код:
int k;
int** test;
printf("Enter a value for k: ");
scanf("%d", &k);
test = (int **)malloc(k * sizeof(int*));
for (i = 0; i < k; i++) {
test[i] = (int*)malloc(k * sizeof(int)); //Initialize all the values
}
Я думал, что в C, чтобы определить массив, который вам нужно было поместить []
после имени, так что же такое int** test
; это не просто указатель на указатель? И строка malloc()
тоже меня пугает.....
Ответы
Ответ 1
В соответствии с объявлением int** test;
, test
является указателем на указатель, а код pice выделяет память для матрицы значений int динамически с использованием функции malloc.
Заявление:
test = (int **)malloc(k * sizeof(int*));
// ^^------^^-------
// allocate for k int* values
Выделите память продолжения для k
указателей на int (int*
). Предположим, что если k = 4
, то вы получите что-то вроде:
temp 343 347 351 355
+----+ +----+----+----+----+
|343 |---►| ? | ? | ? | ? |
+----+ +----+----+----+----+
Я предполагаю, что адреса имеют четыре байта, а ?
означает значения мусора.
temp
переменная, назначенная возвращенным адресом malloc, malloc выделяет оставшиеся блоки памяти размером = k * sizeof(int**)
, что в моем примере = 16 байт.
В цикле for вы выделяете память для k
int и присваиваете возвращенный адрес temp[i]
(местоположение ранее выделенного массива).
test[i] = (int*)malloc(k * sizeof(int)); //Initialize all the values
// ^^-----^^----------
// allocate for k int values
Примечание: выражение temp[i]
== *(temp + i)
. Таким образом, для цикла в каждой итерации вы выделяете память для массива значений k int, которые выглядят примерно так:
First malloc For loop
--------------- ------------------
temp
+-----+
| 343 |--+
+-----+ |
▼ 201 205 209 213
+--------+ +-----+-----+-----+-----+
343 | |= *(temp + 0) | ? | ? | ? | ? | //for i = 0
|temp[0] |-------| +-----+-----+-----+-----+
| 201 | +-----------▲
+--------+ 502 506 510 514
| | +-----+-----+-----+-----+
347 |temp[1] |= *(temp + 1) | ? | ? | ? | ? | //for i = 1
| 502 |-------| +-----+-----+-----+-----+
+--------+ +-----------▲
| | 43 48 52 56
351 | 43 | +-----+-----+-----+-----+
|temp[2] |= *(temp + 2) | ? | ? | ? | ? | //for i = 2
| |-------| +-----+-----+-----+-----+
+--------+ +-----------▲
355 | |
| 9002 | 9002 9006 9010 9014
|temp[3] | +-----+-----+-----+-----+
| |= *(temp + 3) | ? | ? | ? | ? | //for i = 3
+--------+ | +-----+-----+-----+-----+
+-----------▲
Снова ?
означает значения мусора.
Дополнительные точки:
1) Вы возвращаете возвращенный адрес malloc, но на C вы должны его избегать. Прочитайте Я делаю результат malloc?, просто выполните следующие действия:
test = malloc(k* sizeof(int*));
for (i = 0; i < k; i++){
test[i] = malloc(k * sizeof(int));
}
2) Если вы распределяете память динамически, вам необходимо освободить память явным образом, когда ваша работа с ней (после освобождения динамически выделенной памяти вы не сможете получить доступ к этой памяти). Шаги для освобождения памяти для test
будут следующими:
for (i = 0; i < k; i++){
free(test[i]);
}
free(test);
3) Это один из способов выделить память для 2D-матрицы в виде массива массивов, если вы хотите полностью выделить память для всех массивов, проверите этот ответ: Выделите массив памяти 2d в функции С
4) Если описание помогает, и вы хотите учиться на 3D-распределение. Посмотрите этот ответ: Матрица массива String или /3D char
Ответ 2
Помните, что массивы распадаются на указатели и могут использоваться в качестве указателей. И эти указатели могут использоваться как массивы. Фактически, индексирование массива можно рассматривать как арифметику формы или указателя. Например
int a[3] = { 1, 2, 3 }; /* Define and initialize an array */
printf("a[1] = %d\n", a[1]); /* Use array indexing */
printf("*(a + 1) = %d\n", *(a + 1)); /* Use pointer arithmetic */
Оба выхода выше будут печатать второй (индекс 1
) элемент в массиве.
То же самое относится и к указателям, они могут использоваться с арифметикой указателя или использоваться с индексированием массива.
Из вышесказанного вы можете думать о указателе-to-pointer-to.type как массиве массивов типа. Но это не вся правда, поскольку они хранятся по-разному в памяти. Поэтому вы не можете передать массив массивов в качестве аргумента функции, которая ожидает указателя на указатель. Однако после инициализации вы можете использовать указатель на указатель с индексированием массива, как обычные указатели.
Ответ 3
malloc используется для динамического выделения памяти в тестовую переменную, считая * как массив и ** в качестве массива массивов, а вместо передачи по значению указатели используются для ссылки на адрес памяти переменной. Когда вызывается malloc, вы распределяете память на тестовую переменную, получая размер целого числа и умножаясь на количество int, которое пользователь предоставляет, потому что это неизвестно до того, как пользователь вводит это.
Ответ 4
Да, отлично. test
является указателем на указатель, поэтому test[i]
, который эквивалентен записи test + i
, будет указателем. Для лучшего понимания, пожалуйста, посмотрите на c - FAQ.
Ответ 5
Да, int**
- указатель на указатель. Мы также можем сказать, что это массив указателей.
test = (int **) malloc(k * sizeof(int*));
Сначала будет выделен массив указателей k
. malloc
динамически выделяет память.
test[i] = (int*) malloc(k * sizeof(int));
Это необязательно, поскольку этого достаточно для
test[i] = (int*) malloc(sizeof(int*));
Здесь мы выделяем каждое из мест массива для указания на допустимую память. Однако для базовых типов, таких как int
, такого рода распределение не имеет смысла. Это полезно для более крупных типов (структур).
Каждый указатель может быть доступен как массив, и наоборот, например, следующий эквивалент.
int a;
test[i] = &a;
(test + i) = &a;
Это может быть массив test
в памяти, который выделяется начиная со смещения 0x10000000:
+------------+------------+
| OFFSET | POINTER |
+------------+------------+
| 0x10000000 | 0x20000000 | test[0]
+------------+------------+
| 0x10000004 | 0x30000000 | test[1]
+------------+------------+
| ... | ...
Каждый элемент (в этом примере 0x2000000 и 0x30000000) является указателем на другую выделенную память.
+------------+------------+
| OFFSET | VALUE |
+------------+------------+
| 0x20000000 | 0x00000001 | *(test[0]) = 1
+------------+------------+
| ...
+------------+------------+
| 0x30000000 | 0x00000002 | *(test[1]) = 2
+------------+------------+
| ...
Каждое из значений содержит только пространство sizeof (int).
В этом примере test[0][0]
будет эквивалентен *(test[0])
, однако test[0][1]
недействителен, так как он получит доступ к памяти, которая не была назначена.
Ответ 6
Для каждого типа T существует тип "указатель на T".
Переменные могут быть объявлены как указатели на значения различных типов, с помощью декларатора типа *
. Чтобы объявить переменную как указатель, перед ее именем следует звездочка.
Следовательно, "для каждого типа T" также применимо к типам указателей, существуют множественные косвенные указатели, такие как char ** или int *** и т.д. Существуют также типы "указатель на массив", но они менее распространены, чем "массив указателя" (http://en.wikipedia.org/wiki/C_data_types)
поэтому int**
test объявляет массив указателей, который указывает на "int array"
в строке test = (int **)malloc(k*sizeof(int*));
выделяет достаточное количество памяти для k суммы (int*
)
поэтому есть k количество указателей, каждый из которых указывает на...
test[i] = (int*)malloc(k * sizeof(int));
(каждый указатель указывает на массив с размером k значений int)
Резюме...
int** test;
состоит из k количества указателей, каждый из которых указывает на k значений int.
Ответ 7
int ** - указатель на указатель int. посмотрите правило "право-слева"