3

我正在工作并且正在考虑使用工会。我决定反对它,因为设计确实需要一个结构/类,但它最终导致了以下假设性问题:

假设你有一个像这个人为的例子这样的联合:

typedef union {
    char* array_c;
    float* array_f;
    int* array_i;
} my_array;

. . . 然后你分配一个数组并尝试从其他地方删除它:

my_array arr;
arr.array_f = (float*)(malloc(10*sizeof(float)));
free(arr.array_i);

我认为这会起作用,尽管它在技术上没有定义,因为 malloc 的实现方式。我还假设它在分配 array_c 时会起作用,尽管与 int 与 float 不同,数组的大小不太可能相同。

可以使用类似的 new 和 delete 重复测试。我猜这些也行得通。

我猜语言规范会讨厌我这样做,但我希望它会起作用。它让我想起了“即使它是数组而不是对象,也不要删除转换为 void* 的新指针”业务。

所以问题是:规范对这样做有什么看法?我简单地检查了一下,但找不到任何特别解决这种情况的东西。无论如何,这是多么不明智——从功能的角度来看(我意识到从清晰的角度来看这很糟糕)。

这纯粹是出于迂腐目的的好奇问题。

4

3 回答 3

2

你完全正确。它打破了规则:

当一个值存储在联合类型对象的成员中时,不对应于该成员但确实对应于其他成员的对象表示的字节采用未指定的值,但联合对象的值不应因此成为陷阱表示。
    - ISO/IEC 标准 9899,第 6.2.6.1 节

然而,实现的方式通常是完成的,它会“意外地”正常工作。由于free需要 a void *,因此参数将转换为 avoid *以传递给free。由于所有指针都位于同一地址,并且所有到 a 的转换void *都不涉及对其值的更改,因此传递给的最终值free将与传递正确的成员相同。

从理论上讲,如果您读取的成员与上次写入的成员不同,则实现可以跟踪最后访问的联合成员并破坏值(或使程序崩溃或执行其他任何操作)。但据我所知,没有实际的实现会做这样的事情。

于 2012-06-29T04:01:56.257 回答
2

这是未定义的行为,因为您访问的成员与您设置的成员不同。它可以做任何事情。

在实践中,这通常会起作用,但您不能依赖它。编译器和工具链并非故意作恶,但在某些情况下,优化与未定义的行为交互会产生完全出乎意料的结果。当然,如果您曾经使用过具有不同 malloc 实现的系统,它可能会崩溃。

于 2012-06-29T04:02:24.860 回答
-1

它与 malloc() 实现无关。您示例中的联合使用相同的内存位置来存储三个“不同”指针之一。但是,所有指针,无论它们指向什么,都是相同的大小 - 这是您所在架构的本机整数大小 - 在 32 位系统上为 32 位,在 64 位系统上为 64 位等。这是因为指针是内存中的地址,可以用整数表示。

假设您的 arr 位于地址 0x10000(指向您的指针的指针,如果您愿意的话)。假设 malloc() 在 0x666660 处为您找到一个内存位置。您将 arr.array_f 分配给该值 - 这意味着您将指针 0x666660 存储在位置 0x10000 中。然后将浮点数写入 0x666660 到 0x666688。

现在,您尝试访问 arr.array_i。因为您使用的是联合,所以 arr.array_i 的地址与 arr.array_f 和 arr.array_c 的地址相同。所以你再次从地址 0x10000 读取 - 你读出了指针 0x666660。由于这与之前返回的 malloc 指针相同,因此您可以继续释放它。

也就是说,试图将整数解释为文本,或将浮点数解释为整数等,显然会导致破产。如果 arr.array_i[0] == 1,那么 arr.array_f[0] 肯定不会 == 1,并且 arr.array_c[0] 与字符 '1' 无关。您可以尝试以这种方式“查看”内存作为练习(循环和 printf()) - 但您将一事无成。

于 2012-06-29T05:29:50.927 回答