2012-01-06 7 views
4

Этот вопрос является продолжением Malloc call crashing, but works elsewhere«malloc (sizeof (struct a *))» и «malloc (sizeof (struct a))« то же самое?

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

Вот некоторые базовые знания о выделении memory использования malloc() w.r.t 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"). В основном 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" с использованием pointer-to-"struct a"?

data * array = NULL; 

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

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

#include <stdio.h> 
#include <stdlib.h> 

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: OK сказать, если я следующий по ошибке

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

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

+1

Вашего вопрос название, кажется, не соответствуют актуальным вопросам в остальной части текста выше ... –

+0

Кроме того, почему вы хотите назначить результат «malloc» не по той причине, а затем разыменовать его? Я не понимаю предпосылку вопросов. –

+0

@ OliCharlesworth Я делаю это по ошибке и не намеренно! так что разве программа не может упасть, когда я это сделаю ?! –

ответ

4

Существует, по-видимому, фундаментальное недоразумение.

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 элементов типа data, есть три возможности

  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)); 
+0

Спасибо за подробное объяснение. +1 и правильный ответ! :) –

7

Номер

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

+0

Спасибо за ваш ответ. Но не могли бы вы рассказать о том, почему работает этот фрагмент кода? .. Разве это не должно терпеть крах? –

+2

@SangeethSaravanaraj: Неопределенное поведение не означает, что программа всегда будет терпеть крах, это просто означает, что программа плохо сформирована, и вы не можете определить ее поведение. –

+1

@SangeethSaravanaraj: ** Неопределенное поведение ** - это то, что должно произойти. –

1

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

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

1

Возможно ли присвоить это структуре a * вместо struct a **?

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

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

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

+1

Конечно, безопасно отображать (или напрямую назначать) результат вызова 'malloc()' в «неправильный» тип указателя. 'malloc()' не имеет понятия типов, он просто выделяет последовательности байтов. Неопределенное поведение возникает только в результате записи в конце последовательности байтов, которые вы задали 'malloc()', чтобы дать вам. –