【问题标题】:Why do we need to declare a variable of union type when nested in a structure in C?为什么我们需要在 C 中嵌套在结构中时声明联合类型的变量?
【发布时间】:2020-06-26 18:22:45
【问题描述】:

我有一个教程中的代码示例,上面写着

struct goods {
    char name[20];
    union quantity {
        int count;
        float weight, volume;
    } q;
};

我不明白为什么我们需要声明“q”变量以及联合类型名称“数量”?为什么我们不能只使用“数量”,然后通过点访问结构字段?


更新:“数量”是联合类型的名称/标签是否正确,而“q”不是变量,而是包含子成员(计数、重量、体积)?

【问题讨论】:

  • 这样做可以节省内存使用量。
  • @TogtokhKhorchin OP 询问为什么要命名联合,而不是为什么要使用它。
  • 显然代码是在匿名联合 were allowed 之前编写的 some time ago
  • union quantity 是类型,q 是字段。
  • @RobertSsupportsMonicaCellio:是的,它是正确的。 qstruct goods 的成员(在某些情况下也称为字段),q 包含子成员。该问题并未询问或暗示q 是否是“工会本身的成员”。

标签: c struct unions


【解决方案1】:

不清楚您对这段代码提出了什么具体问题,所以让我们回顾一下这些问题。

结构成员

union 声明出现在struct 声明中时,它通常声明该结构的一个成员是一个联合。该成员是结构的一部分,与任何其他成员相同,例如在结构中声明的int x。结构的每个实例都包含其每个成员的实例,包括联合——联合是结构的一部分,而不是单独的事物。

名字

在这段代码中:

    union quantity {
        int count;
        float weight, volume;
    } q;

标识符quantity 是联合的标签。在此角色中,它必须出现在 union 关键字之后,始终为 union quantity。它只命名联合type;它没有命名任何联合 objectmember 结构。 (相同的标识符可以用于多个角色。我们也可以添加一个声明,将quantity 定义为类型或对象或成员,然后它将有两个角色:它可以是用作union quantity 来引用联合类型,它本身可以用来引用其他声明的任何声明。)

在上面的相同代码中,q 是结构成员的名称。它是struct goods 的每个实例中的union quantity 对象的名称。

有了这个声明,如果我们定义一个struct goods G;,那么G.q指的是G中的union quantity,而G.q.countG.q.weightG.q.volume指的是在工会G.q。 (一次只能存储其中一个成员,因为它们都在一个联合中重叠。)

匿名联合

在 C 2011 中,添加了一项新功能。 unionstructure 可以在另一个 unionstructure 内声明而没有成员名称:

struct goods {
    char name[20];
    union {
        int count;
        float weight, volume;
    };
};

这根本不会改变结构的布局——它仍然有相同的成员。但是,它们的名称不同。给定struct goods G,我们可以将count 成员称为G.count 而不是G.q.countweightvolume 也是如此。 (请注意,除了删除成员名称q,此代码还删除了标签quantity。C 标准中有一条规则规定,结构或联合必须是匿名的,它不能有标记以及没有成员名称。我不认为这是技术原因。也许这是避免成员名称被无意遗漏的错误的选择。)

至于为什么有人会给联合成员一个名字而不是匿名,原因之一是代码是在 2011 年之前或 2011 年之后编写的,但用于尚不支持匿名成员的 C 实现。另一个原因是他们想要区分联合成员,因此任何阅读或编写代码的人都会注意到这些成员位于结构内部的某个东西中,而不是常规的直接结构成员。

【讨论】:

  • 感谢您的全面回复。所以在上面的代码示例中,'q' 确实代表具有子成员的结构成员的名称,而不是变量?如果是这样,那完美地澄清了我的一切。 .
【解决方案2】:

我不明白为什么我们需要声明“q”变量以及联合类型名称“数量”?

如问题中所述,struct goods 是具有两个成员的结构类型:由 name 标识的 20 个 char 数组和由 q 标识的 union quantity (所以是的,quantity 是一个联合标签,而不是成员的名字)。绝对没有必要以这种方式声明它,但这样的声明提供了其他替代方案所没有的一些特征。但是,请理解,正如示例中所声明的,countweightvolume 不是 struct goods 的成员。相反,他们是 q 的成员,该工会是 struct goods 的成员。

为什么我们不能只使用“数量”,然后通过点访问结构字段?

因为这不是 C 语法提供的替代方案之一。在结构类型声明的成员列表中,联合标记(在这种情况下为quantity)只能出现在 named 成员的声明中,因此如果提供了,那么您还必须声明一个联合标识符——示例中为q。并且已将联合声明为命名成员,您必须通过联合的标识符访问 成员。

另一方面,你可以省略标签,如果你这样做了,那么,你也可以选择省略联合的标识符。如果您确实省略了标识符(并且仅在这种情况下),您将拥有一个“匿名联合成员”,其自己的成员被访问就好像他们实际上是包含结构的成员。这与您的要求非常接近。

请注意,无论如何,联合的成员彼此共享存储空间,因此联合在任何给定时间都只包含其中一个。它们不与包含结构的其他成员共享存储空间。

话虽如此,各种选项的特性确实存在一些差异。首先,要意识到所有这些形式都具有双重意义:它们声明了一个联合类型,它们声明了一个该类型的结构成员。这是相关的,因为如果您提供一个标签,那么您可以在联合声明在范围内的任何地方声明相同联合类型的其他对象。此外,该范围不限于包含它的结构类型声明,因此通过提供的声明,可以执行以下操作:

void set_quantity(struct goods *g, union quantity quant) {
    g->q = quant;
}

这对于未标记的工会来说是不可能的。

具有未标记联合类型的命名成员和匿名联合成员之间至少还有一个重要区别:只有当联合被命名时,您才能访问联合本身。考虑一下:

struct goods2 {
    char name[20];
    union {
        int count;
        float weight, volume;
    } q;
};

void copy_quantity(struct goods2 *dest, struct goods2 *src) {
    dest->q = src->q;
}

你不仅不能对匿名工会成员这样做,而且你不能做任何可靠的等价物。特别是,即使您愿意承受与单独复制 src->countsrc->weightsrc->volume 相关的低效率,尽管其中只有一个实际上包含值,但 C 不承诺在任何情况下这样做order 将可靠地达到预期的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 2021-10-28
    • 2011-11-02
    • 1970-01-01
    相关资源
    最近更新 更多