在以下代码中,还会释放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?
}
答案 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)
如果您希望能够单独释放乳制品,您可以用乳制品替代酸奶中的乳制品*。不过,你还必须单独分配它。