«Malloc (sizeof (struct a *))» и «malloc (sizeof (struct a))« то же самое?

Этот вопрос является продолжением вызова Mallok, но работает в другом месте

Я пробовал следующую программу, и я нашел ее работоспособной (т. Е. Не сбой), и это было упомянуто в вышеупомянутой ссылке тоже). Мне может быть повезло, что он работает, но я ищу разумное объяснение экспертов SO о том, почему это работает ?!

Вот несколько основных соображений по распределению memory с помощью malloc() wrt structures и pointers

  • malloc(sizeof(struct a) * n) выделяет n элементов типа struct a . И эту ячейку памяти можно сохранить и получить с помощью pointer-to-type-"struct a" . В основном struct a * .
  • malloc(sizeof(struct a *) * n) выделяет n число элементов struct a * . Затем каждый элемент может указывать на элементы типа struct a . В основном malloc(sizeof(struct a *) * n) выделяет array(n-elements)-of-pointers-to-type-"struct a" . И выделенное место памяти может быть сохранено и доступно с помощью pointer-to-(pointer-to-"struct a") объект pointer-to-(pointer-to-"struct a") . В принципе struct a ** .

Поэтому, когда мы создаем array(n-elements)-of-pointers-to-type-"struct a" , это

  1. допустимо ли присваивать это struct a * вместо struct a ** ?
  2. действительный для доступа / удаления ссылки на выделенный array(n-elements)-of-pointers-to-type-"struct a" используя array(n-elements)-of-pointers-to-type-"struct a" pointer-to-"struct a" ?

 data * array = NULL; if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) { printf("unable to allocate memory \n"); return -1; } 

Фрагмент кода выглядит следующим образом:

 #include  #include  int main(void) { typedef struct { int value1; int value2; }data; int n = 1000; int i; int val=0; data * array = NULL; if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) { printf("unable to allocate memory \n"); return -1; } printf("allocation successful\n"); for (i=0 ; i<n ; i++) { array[i].value1 = val++; array[i].value2 = val++; } for (i=0 ; i<n ; i++) { printf("%3d %3d %3d\n", i, array[i].value1, array[i].value2); } free(array); printf("freeing successful\n"); return 0; } 

EDIT: ОК, если я сделаю следующее по ошибке

 data * array = NULL; if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) { 

Есть ли способ захвата (во время компиляции с использованием любых флагов GCC ) такого рода непреднамеренное программирование опечаток, которое могло бы работать время от времени и могло бы взорваться в любое время! Я скомпилировал это с помощью -Wall и не обнаружил никаких предупреждений!

Кажется, есть фундаментальное недоразумение.

malloc (sizeof (struct a) * n) выделяет n элементов типа struct a.

Нет, это именно то, что обычно использует, как после такого звонка. malloc(size) выделяет область памяти size байтов. То, что вы делаете с этим регионом, полностью зависит от вас. Единственное, что имеет значение, это то, что вы не переступаете границы выделенной памяти. Предполагая 4 байта float и int и 8 байтов double , после успешного malloc(100*sizeof(float)); , вы можете использовать первые 120 из 400 байтов как массив из 15 double s, а следующий 120 – как массив из 30 float s, затем поместите массив из 20 char s за ним и заполните оставшиеся 140 байтов 35 int s, если хотите. Это совершенно безобидное определенное поведение.

malloc возвращает void* , который может быть неявно передан указателю любого типа, поэтому

 some_type **array = malloc(100 * sizeof(data *)); // intentionally unrelated types 

отлично, это может быть просто не объем памяти, который вы хотели. В этом случае это очень вероятно, потому что указатели имеют одинаковый размер независимо от того, на что они указывают.

Скорее всего, вы получите неправильный объем памяти.

 data *array = malloc(n * sizeof(data*)); 

как у вас. Если вы используете выделенную часть памяти в виде массива из n элементов данных типа, существует три возможности

  1. sizeof(data) < sizeof(data*) . Тогда ваша единственная проблема в том, что вы теряете пространство.
  2. sizeof(data) == sizeof(data*) . Все в порядке, нет пространства впустую, как будто у вас не было никакой опечатки.
  3. sizeof(data) > sizeof(data*) . Затем вы получите доступ к памяти, к которой вы не должны обращаться, когда касаетесь более поздних элементов массива, что является неопределенным поведением. В зависимости от различных вещей, которые могли бы последовательно работать так, как если бы ваш код был правильным, немедленно сбой с segfault или что-то среднее между ними (технически он мог вести себя так, что не может быть значимо размещен между этими двумя, но это было бы необычно).

Если вы намеренно делаете это, зная точку 1. или 2., это плохая практика, но не ошибка. Если вы делаете это непреднамеренно, это ошибка, независимо от того, какая точка применима, безвредна, но трудно найти, в то время как 1. или 2. применяется, вредно, но обычно легче обнаружить в случае 3.

В ваших примерах. data было 4 соответственно. 8 байтов (возможно), которые на 64-битной системе помещают их в 1, соответственно. 2. с большой вероятностью, на 32-битной системе на 2 соответственно. 3.

Рекомендуемый способ избежать таких ошибок -

 type *pointer = malloc(num_elems * sizeof(*pointer)); 

Нет.

sizeof(struct a*) – размер указателя .
sizeof(struct a) – это размер всей структуры.

Этот array = (data *)malloc(sizeof(data *) * n) выделяет sizeof(data*) ( указатель ) на data структуры, если вы хотите это сделать, вам нужен array который будет data** array ,

В вашем случае вы хотите, чтобы ваш указатель указывал на sizeof(data) , структуру в памяти, а не на другой указатель. Для этого потребуются data** (указатель на указатель).

допустимо ли это присваивать структуре a * вместо struct a **?

Ну, с технической точки зрения, это действительно так, но это неправильно (UB), чтобы разыменовать такой указатель. Вы не хотите этого делать.

действительный для доступа / удаления ссылки на выделенный массив (n-elements) -of-указатели-на-type- “struct a”, используя указатель-to-struct a?

Нет, неопределенное поведение.