C - освобождение «унаследованной» структуры - PullRequest
2 голосов
/ 08 февраля 2012

В следующем коде освободит также Dairy бесплатно Yogurt?
Насколько я знаю, оба указывают на один и тот же адрес.

Кроме того, этот стиль кодирования - плохая практика? Скажите, если бы я только сохранил указатели на 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 ]

4 голосов
/ 08 февраля 2012

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

В соответствии со стандартом 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

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

2 голосов
/ 08 февраля 2012

Здесь есть связанная дискуссия: Этот код гарантирован стандартом C?

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

1 голос
/ 08 февраля 2012

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

C99 6.7.2.1/13 Спецификаторы структуры и объединения

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

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

0 голосов
/ 08 февраля 2012

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

0 голосов
/ 08 февраля 2012

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

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

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

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

...