【问题标题】:union of structs sharing same first members共享相同第一个成员的结构联合
【发布时间】:2015-03-09 11:08:43
【问题描述】:

我一直在研究在 C11 C 之前的 C 中实现结构“多态性”的非传统方式。假设我们有 2 个结构:

struct s1 {
    int var1;
    char var2;
    long var3;
};

struct s2 {
    int var1;
    char var2;
    long var3;
    char var4;
    int var5;
};

在大多数编译器上,我们可以安全地在指向两者的指针之间进行强制转换,然后在没有发生填充的情况下访问公共的第一个成员。但是,这不是标准化的行为。

现在,我在 C 标准中找到了 C89 的以下行:

为了简化联合的使用,有一个特殊的保证:如果联合包含多个共享一个共同初始序列的结构,并且如果联合对象当前包含这些结构之一,则允许检查共同初始其中任何一个的一部分。如果对应的成员具有一个或多个初始成员序列的兼容类型,则两个结构共享一个共同的初始序列。

它还声明了以下内容:

一个指向联合对象的指针,适当地转换,指向它的每个成员(或者如果一个成员是一个位域,那么指向它所在的单元),反之亦然。

现在,如果我创建这两个结构的联合:

union s2_polymorphic {
    struct s1 base;
    struct s2 derived;
};

并以这种方式使用它:

union s2_polymorphic test_s2_polymorphic, *ptest_s2_polymorphic;
struct s2 *ptest_s2;
struct s1 *ptest_s1;

ptest_s2_polymorphic = &test_s2_polymorphic;

ptest_s2 = (struct s2*)ptest_s2_polymorphic;

ptest_s2->var1 = 1;
ptest_s2->var2 = '2';

ptest_s1 = (struct s1*)ptest_s2;

printf("ptest_s1->var1 = %d\n", ptest_s1->var1);
printf("ptest_s1->var2 = %c\n", ptest_s1->var2);

编译和运行良好,并在 gcc (GCC) 4.8.3 20140911 上提供输出

ptest_s1->var1 = 1                                                            
ptest_s1->var2 = 2

根据上面给出的标准的引用,行为是否得到明确定义?

【问题讨论】:

  • 我可能误解了您在这里所做的事情,但在union 的示例中,不应该s2(即derived)现在不复制s1 的内容吗? IE不应该只包含额外的元素吗?
  • "行为是否明确" 我会说:是的。是什么让你怀疑这一点?
  • @abligh:我可能误解了你的问题......你所说的“只包含额外的元素”是什么意思?
  • @Mints97 其实这种方法很常见。通常第一个成员用于指定实际类型。这被称为“智能联合”
  • @Mints97 考虑一下内存使用、多重继承和信息隐藏,您也会看到这种方法的一些缺点,不是吗?

标签: c pointers struct unions


【解决方案1】:

经过一番研究,我想我对这个问题有一个合格的答案。

给出的引用来自 C89 标准。 C99 和 C11 改写如下:

为了简化联合的使用,我们做了一个特殊的保证:如果联合包含多个共享相同初始序列的结构(见下文),并且如果联合对象当前包含这些结构之一,则允许检查其中任何一个的公共初始部分在任何地方可以看到已完成联合类型的声明。

最后一部分可以以多种方式解释,恕我直言。然而,委员会保持原样。根据他们的说法,这意味着对结构的“公共初始部分”的检查只能使用已声明包含它们的union 类型的对象来完成。 this question 很好地证明了这一点。

C89 怎么样,它似乎允许我尝试做的事情?好吧,在符合 C89 的编译器中,是的,这应该可以工作。但是,它可能并不真正需要:我不知道有一个支持严格别名的严格 C89 兼容编译器,因此,使用它们,更容易将具有共同初始序列的结构简单地转换为彼此的类型并尽量不要给他们不同的包装设置。结果应该是一样的。

【讨论】:

  • 鉴于联合体的完整类型需要可见才能通过它访问任何内容,为什么标准的作者会对联合体的完整类型的可见性发表任何意见?他们不打算允许联合声明将关联类型“绑定”为一个应该允许相互检查彼此的共同成员的组?我知道 gcc 的作者不喜欢这条规则,并且会有更好的方法来实现预期的结果,但是可见联合中的成员资格是标准的作者选择允许这种语义的方式。
  • 如果标准明确允许将指向任何类型的指针强制转换为指向包含该类型的联合的指针,前提是它仅用于访问正确的联合成员或其他具有共同的初始序列,并且如果它指定通过共享任何成员的两个联合类型的操作可以别名,这可能是提供正确功能的好方法,但是如果一个联合成员的对齐比另一个更粗略,则将指针转换为与联合指针松散对齐的对象会调用 UB,因为它不满足联合的对齐方式。
猜你喜欢
  • 2014-01-12
  • 2020-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-13
相关资源
最近更新 更多