C - Freeing'继承'结构体

时间:2012-02-08 07:33:01

标签: c malloc free

在以下代码中,还会释放Dairy免费Yogurt吗?  据我所知,两者都指向相同的地址。

另外,这种编码风格是不是很糟糕?如果我只保留指向Dairy的指针,并间接释放YogurtCheese

#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 个答案:

答案 0 :(得分: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

  

在结构对象内,非位字段成员和位域中的单位   驻留的地址按声明的顺序增加。 指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单位),反之亦然。可能有未命名的   在结构对象中填充,但不在其开头。

答案 1 :(得分:2)

这里有一个相关的讨论: Is this code guaranteed by the C standard?

它指的是C标准以及关于填充的定义。填充从未在结构的开头完成,因此代码将起作用。我甚至会说这是很多C代码中常见的模式。

答案 2 :(得分:1)

指向structy的指针和指向struct的第一个成员的指针必须指向同一个地址:

C99 6.7.2.1/13结构和联合说明符

  

指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。在结构对象中可能有未命名的填充,但不是在它的开头。

所以,你可以free()表达,虽然我不会以所示的风格推荐它,因为它看起来相当脆弱,让读者感到困惑,我想。但是如果你实现了一种基于C语言的API,它期望一个“基类”指针并期望能够释放它,那么我可以看到它有意义(并且很有用)。

答案 3 :(得分:0)

会的。 free使用void指针,并释放任何你告诉它释放的内容。由于这是C,与C ++相比,没有令人担心的析构函数

请注意,这只能起作用,因为在{inherited“结构中首先声明了Dairy。如果您更改声明顺序,则此功能将不再起作用。

编辑:第二个想法,你不能依赖于此。您不知道可能会发生什么样的编译器魔法以及可能会在结构中偏移Dairy成员的包装。它可能会起作用,但我不确定你是否能保证它始终有效。

可以很自然地说这是不好的做法,可能会导致以后的可移植性问题。特别是对于以不同方式打包结构并使用不同的本机字对齐的平台。它也可能与#pragma pack__declspec(align())

混淆

答案 4 :(得分:0)

如果您希望能够单独释放乳制品,您可以用乳制品替代酸奶中的乳制品*。不过,你还必须单独分配它。