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

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