C - 释放“继承的”结构

发布于 2025-01-03 17:20:16 字数 472 浏览 3 评论 0原文

在下面的代码中,释放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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

何其悲哀 2025-01-10 17:20:16

是的,行为是明确定义的。

根据 C 标准,您需要将与 malloc 或其他分配函数返回的地址完全相同的地址传递给释放函数 free

参考:

7.22.3.3 免费功能

概要:

§1 #include; void free(void *ptr);

描述:

§2 free 函数导致 ptr 指向的空间被释放,即
可供进一步分配。如果 ptr 是空指针,则不会发生任何操作。否则,如果参数与内存管理先前返回的指针不匹配
函数,或者如果空间已通过调用 free 或 realloc 释放,则
行为未定义。

因此,在这种情况下,当且仅当指向结构的指针与指向其第一个成员的指针相同时,才能保证行为得到良好定义。
这是由以下标准保证的:

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 an malloc or other allocation functions.

Reference:

7.22.3.3 The free function

Synopsis:

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

Description:

§2 The free function causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management
function, or if the space has been deallocated by a call to free or realloc, the
behavior is undefined
.

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

Within a structure object, the non-bit-field members and the units in which bit-fields
reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed
padding within a structure object, but not at its beginning.

挽梦忆笙歌 2025-01-10 17:20:16

这里有一个相关的讨论:
此代码是否受 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.

儭儭莪哋寶赑 2025-01-10 17:20:16

指向结构体的指针和指向结构体第一个成员的指针必须指向同一地址:

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

A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

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.

零時差 2025-01-10 17:20:16

会的。 free 接受一个 void 指针,并将释放您告诉它释放的任何内容。由于这是 C 语言,因此无需担心析构函数,与 C++ 相比

请注意,这只有效,因为 Dairy 在您的“继承”结构中首先声明。如果您更改声明顺序,这将不再起作用。

编辑:再想一想,你不能依赖这一点。您不知道编译器可能会发生什么魔法,也不知道可能发生什么打包可能会抵消结构中的 Dairy 成员。它可能会起作用,但我不确定你是否能保证它始终有效。

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

It will. free takes a void 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())

乖不如嘢 2025-01-10 17:20:16

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文