Динамическое распределение массива объясняет

Это пример кода, который мой учитель показал нам: «Как динамически выделять массив в 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() также меня смущает …..

Согласно заявлению 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 variable присваивает возвращенный адрес malloc, malloc выделяет оставшиеся блоки памяти size = 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-массива в функции C

4) Если описание помогает, и вы хотите учиться на 3D-распределение. Посмотрите этот ответ: Матрица массива String или / 3D

Помните, что массивы распадаются на указатели и могут использоваться в качестве указателей. И эти указатели могут использоваться как массивы. Фактически, индексирование массива можно рассматривать как арифметику формы или указателя. Например

 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 как массиве массивов типа. Но это не вся правда, поскольку они хранятся по-разному в памяти. Поэтому вы не можете передать массив массивов в качестве аргумента функции, которая ожидает указателя на указатель. Однако после инициализации вы можете использовать указатель на указатель с индексированием массива, как обычные указатели.

malloc используется для динамического выделения памяти в тестовую переменную, считая * как массив и ** как массив массивов, а вместо передачи по значению указатели используются для ссылки на адрес памяти переменной. Когда вызывается malloc, вы распределяете память на тестовую переменную, получая размер целого числа и умножая на количество ints, которое пользователь предоставляет, потому что это неизвестно до того, как пользователь вводит это.

Да, это нормально. test является указателем на указатель, поэтому test[i] который эквивалентен написанию test + i будет указателем. Для лучшего понимания, пожалуйста, ознакомьтесь с этим с – FAQ .

Да, 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 |  УКАЗАТЕЛЬ |
 + ------------ + ------------ +
 |  0x10000000 |  0x20000000 |  Тест [0]
 + ------------ + ------------ +
 |  0x10000004 |  0x30000000 |  Тест [1]
 + ------------ + ------------ +
 |  ... |  ...

Каждый элемент (в этом примере 0x2000000 и 0x30000000) является указателем на другую выделенную память.

 + ------------ + ------------ +
 |  OFFSET |  VALUE |
 + ------------ + ------------ +
 |  0x20000000 |  0x00000001 |  * (тест [0]) = 1
 + ------------ + ------------ +
 |  ...
 + ------------ + ------------ +
 |  0x30000000 |  0x00000002 |  * (тест [1]) = 2
 + ------------ + ------------ +
 |  ...

Каждое из значений содержит только пространство для sizeof (int).

В этом примере test[0][0] будет эквивалентен *(test[0]) , однако test[0][1] недействителен, поскольку он будет иметь доступ к памяти, которая не была выделена.

Для каждого типа T существует тип «указатель на T».

Переменные могут быть объявлены как указатели на значения различных типов с помощью декларатора типа * . Чтобы объявить переменную как указатель, перед ее именем следует указать звездочку.

Следовательно, «для каждого типа T» также применимо к типам указателей, существуют множественные косвенные указатели, такие как char ** или int *** и т. Д. Существуют также типы «указатель на массив», но они менее распространены, чем «массив указателей» ( http://en.wikipedia.org/wiki/C_data_types )

поэтому int** test объявляет массив указателей, который указывает на «int-массивы»,

в строке test = (int **)malloc(k*sizeof(int*)); кладет достаточно памяти в сторону для k суммы ( int* ) ‘s

поэтому есть k количество указателей, каждый из которых указывает на …

test[i] = (int*)malloc(k * sizeof(int)); (каждый указатель указывает на массив с размером k значений int)

Резюме…

int** test; состоит из k количества указателей, каждый из которых указывает на k количество int.

int ** – указатель на указатель int. взгляните на правило «вправо-влево»