2012-02-08 4 views
2

В следующем коде будет высвобождаться Dairy бесплатно Yogurt?
Оба указывают на тот же адрес, насколько я знаю.C - Freeing 'inherited' structure

Кроме того, этот стиль кодирования плохой практики? Скажите, если я только указал указатели на Dairy и косвенно освободил Yogurt и Cheese?

#include <stdlib.h> 

typedef struct { 
    int calcium; 
    int protein; 
} Dairy; 

typedef struct { 
    Dairy dairy; 
    int sugar; 
    int color; 
} Yogurt; 

int main() { 
    Yogurt* yogurt = malloc(sizeof(Yogurt)); 

    Dairy* dairy = &yogurt->dairy; 

    free(dairy); // Will this free yogurt? 
} 

ответ

5

Да, поведение четко определено.

Согласно стандарту C Вам необходимо передать тот же адрес функции освобождения free, как было возвращено malloc или другими функциями распределения.

Ссылка:

7.22.3.3 Свободная функция

Сводка:

§1 #include <stdlib.h> void free(void *ptr);

Описание:

§2 Свободная функция заставляет освободить пространство, на которое указывает ptr, то есть сделать доступным для дальнейшего размещения. Если ptr является нулевым указателем, никаких действий не происходит. В противном случае , если аргумент не соответствует указателю, ранее возвращенному функцией управления памятью , или если пространство было освобождено вызовом free или realloc, поведение не определено.

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

6.7.2.1 Структура и накидных спецификаторах
§15

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

+0

Указатели на молочные продукты и йогурты соответствовали тому, когда я их печатал. Смогу ли я полагаться на это или это специфический компилятор? –

+0

@GM: его четко определен, Обновлен справочник со стандарта. –

+0

Спасибо за ваш ответ! –

0

Будет. free принимает указатель void и освободит все, что вы скажете, чтобы бесплатно. Поскольку это C, деструкторов не беспокоит, в отличие от C++

Обратите внимание, что это работает только потому, что Dairy объявлен первым в вашей «унаследованной» структуре. Если вы измените порядок объявления, это больше не будет функционировать.

Редактировать: На второй взгляд вы не можете положиться на это. Вы не знаете, что может произойти с маской компилятора, и что может произойти упаковка, которая может привести к смещению члена Dairy в вашей структуре. Это, вероятно, будет работать, но я не уверен, что вы всегда будете работать.

Можно с уверенностью сказать, что это плохая практика и может привести к проблемам с переносимостью позже. Особенно для платформ, которые структурируют структуру по-разному и используют другое родное выравнивание слов. Он также может быть перепутано с #pragma pack и __declspec(align())

2

Существует похожее обсуждение здесь: Is this code guaranteed by the C standard?

Это со ссылкой на стандарт C и то, что определяется в отношении заполнения. Заполнение никогда не выполняется в начале структуры, поэтому код будет работать. Я бы даже сказал, что это довольно распространенная картина, встречающаяся во многих C-кодах.

+0

Спасибо, я не заметил этого сообщения. Приятно слышать, что этот шаблон используется, но я не решаюсь использовать его, потому что он кажется хрупким, как говорит Майкл Берр. –

1

Указатель на structy и указатель на первый член структуры должен указывать на тот же адрес:

С99 6.7.2.1/13 Структура и накидные спецификаторов

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

Таким образом, вы можете free() либо выражение, хотя я бы не рекомендовал его в стиле показанного, поскольку он кажется довольно хрупким и сбивает с толку читателей, я думаю. Но я мог видеть, что это имеет смысл (и полезно), если вы реализуете своего рода объектные API на основе C, которые ожидают указатель «базового класса» и ожидают, что смогут его освободить.

0

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