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

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

7 ответов

Решение

Согласно декларации int** test;, test это указатель на указатель, и фрагмент кода, выделяющий память для матрицы значений 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), Так что в цикле for в каждой итерации вы выделяете память для массива значений 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) Если описание помогает и вы хотите научиться выделять трехмерные объекты, проверьте этот ответ: матрица строк или массив трехмерных символов

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

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) элемент в массиве.

То же самое относится и к указателям, они могут использоваться с арифметикой указателей или с индексированием массивов.

Исходя из вышесказанного, вы можете думать о pointer-to-pointer-to.type как массиве-типа-массива-типа. Но это не вся правда, так как они по-разному хранятся в памяти. Таким образом, вы не можете передать массив массивов в качестве аргумента функции, которая ожидает указатель на указатель. Однако после инициализации вы можете использовать указатель на указатель с индексированием массива, как обычные указатели.

Да, в самом деле, 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:

+ ------------ + ------------ +
| СМЕЩЕНИЕ | ПУНКТ
+------------+------------+
| 0x10000000 | 0x20000000 | Тест [0]
+------------+------------+
| 0x10000004 | 0x30000000 | Тест [1]
+------------+------------+
| ...        | ...

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

+ ------------ + ------------ +
| СМЕЩЕНИЕ | ЦЕННОСТЬ |
+------------+------------+
| 0x20000000 | 0x00000001 | *(тест [0]) = 1
+------------+------------+
| ...
+------------+------------+
| 0x30000000 | 0x00000002 | *(тест [1]) = 2
+------------+------------+
| ...

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

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

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

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

Для каждого типа 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** test; состоит из k количество указателей, каждый из которых указывает на k количество целых.

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

Другие вопросы по тегам