C - 释放“继承的”结构
在下面的代码中,释放Dairy
也会释放Yogurt
吗?
据我所知,两者都指向同一个地址。
另外,这种编码风格是一种不好的做法吗?假设我只保留指向乳制品的指针并间接释放酸奶和奶酪?
#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?
}
In the following code, will freeing Dairy
free Yogurt
as well?
Both point to the same address as far as I know.
Also, is this style of coding a bad practice? Say if I only kept pointers to the Dairy
and indirectly freed the Yogurt
and Cheese
as well?
#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?
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
是的,行为是明确定义的。
根据 C 标准,您需要将与
malloc
或其他分配函数返回的地址完全相同的地址传递给释放函数free
。参考:
因此,在这种情况下,当且仅当指向结构的指针与指向其第一个成员的指针相同时,才能保证行为得到良好定义。
这是由以下标准保证的:
6.7.2.1 结构和联合说明符
§15
Yes the behavior is well-defined.
As per the C Standard You need to pass the exact same address to deallocation function
free
as was returned by anmalloc
or other allocation functions.Reference:
So, In this case the behavior is guaranteed to be well defined if and only if pointer to the structure is same as pointer to it's first member.
And that is guaranteed by the standard in:
6.7.2.1 Structure and union specifiers
§15
这里有一个相关的讨论:
此代码是否受 C 标准保证?
它指的是C 标准以及关于填充的定义。填充永远不会在结构体的开头完成,因此代码将起作用。我什至可以说,这是在许多 C 代码中非常常见的模式。
There is a related discussion here:
Is this code guaranteed by the C standard?
It's referring to the C standard and what is defined with regards to padding. Padding is never done at the beginning of a struct, thus the code will work. I would even go as far as to say that this is a quite common pattern found in a lot of C-code.
指向结构体的指针和指向结构体第一个成员的指针必须指向同一地址:
C99 6.7.2.1/13 结构体和联合说明符
因此,您可以
free()
任一表达式,但我不建议采用所示的样式,因为我认为它对读者来说似乎相当脆弱且令人困惑。但如果您正在实现一种基于 C 对象的 API,该 API 需要“基类”指针并希望能够释放它,那么我认为它是有意义的(并且很有用)。A pointer to a structy and a pointer to the first member of a struct must point to the same address:
C99 6.7.2.1/13 Structure and union specifiers
So, you can
free()
either expression, though I wouldn't recommend it in the style shown since it seems rather fragile and confusing to readers, I think. But I could see it making sense (and being useful) if you're implementing a kind of C object-based APIs that expect a 'base class' pointer and expect to be able to free it.会的。
free
接受一个void
指针,并将释放您告诉它释放的任何内容。由于这是 C 语言,因此无需担心析构函数,与 C++ 相比请注意,这只有效,因为
Dairy
在您的“继承”结构中首先声明。如果您更改声明顺序,这将不再起作用。编辑:再想一想,你不能依赖这一点。您不知道编译器可能会发生什么魔法,也不知道可能发生什么打包可能会抵消结构中的
Dairy
成员。它可能会起作用,但我不确定你是否能保证它始终有效。可以肯定地说,这是不好的做法,并且可能会在以后导致可移植性问题。特别是对于以不同方式打包结构并使用不同本机字对齐的平台。它也可能会被 #pragma pack 和 __declspec(align())
It will.
free
takes avoid
pointer, and will free whatever it is that you tell it to free. Since this is C there are no destructors to worry about, in contrast to C++Note that this only works because
Dairy
is declared first in your "inherited" struct. If you change the declaration order, this will no longer be functional.Edit: On second thought, you cannot rely on this. You have no idea what compiler magic might happen and what packing might happen that might offset the
Dairy
member in your struct. It will probably work, but I'm not sure you're guaranteed to have it working always.It's pretty safe to say that this is bad practice and might result in portability problems later on. Especially for platforms that pack structs differently and use different native word alignment. It might also be messed up with #pragma pack and __declspec(align())
如果您希望能够单独释放乳制品,您可以将酸奶中的乳制品替换为乳制品*。不过,您还必须单独分配它。
If you wanted to be able to free the Dairy separately you could replace the Dairy in Yogurt with a Dairy*. You'd also have to allocate it separately, though.