在C11中,是否可以通过联合中的一个名为“common”的结构体来访问共享字段?

编程语言 2026-07-10

我正在用C11构建一个带标签的联合(tagged union)来表示不同的测量值。为了在每次读取像value这样的共享字段时不必写switch语句,我在联合中引入了一个CommonData_t结构体。

#include <stdio.h>

typedef enum { CURRENT_ID_1, CURRENT_ID_2 } CurrentId_t;
typedef enum { VOLTAGE_ID_1, VOLTAGE_ID_2 } VoltageId_t;

typedef struct {
    int   id;     // Generic integer type
    float value;  // Shared data
} CommonData_t;

typedef struct {
    CurrentId_t id;
    float       value;
} CurrentData_t;

typedef struct {
    VoltageId_t id;
    float       value;
} VoltageData_t;

// The Tagged Union
typedef struct {
    enum { COMMON, CURRENT, VOLTAGE } kind;
    union {
        CommonData_t  common;
        CurrentData_t current;
        VoltageData_t voltage;
    };
} Measurement_t;

我的理解是,C11 §6.5.2.3条在它们共享一个公共初始序列的前提下,允许从一个非最后写入的联合成员读取。但标准也提到这些类型必须是“兼容的”。

我的问题:

  • CurrentId_t、VoltageId_t和 int被视为“兼容类型”吗?
  • 我对标准的理解正确吗?

解决方案

Are CurrentId_t, VoltageId_t, and int considered "compatible types"?

否。

C标准以构造性的方式规定了兼容类型的规则:两种类型只有在标准中的某些声明明确说它们是兼容的时才是兼容的。这些声明散布在标准各处。标准中“兼容”的要义是:两种类型如果可以被完备成相同的类型,则它们是兼容的。例如,指向未指定长度的 int 数组的指针,可以被完成成指向长度为10的 int 数组的指针,因此这两种类型是兼容的。

在C 2011标准中没有任何声明说同一翻译单元中声明的两个枚举是兼容的,因此它们并不兼容。也没有声明任何枚举与 int 兼容。

C 2011草案N1570 6.2.7指定了两个枚举要兼容所需满足的条件:

… Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: … For two enumerations, corresponding members shall have the same values…

C 2024 6.2.7确实规定同一翻译单元中的枚举也可以是兼容的,但这对你并没有帮助,因为它要求它们具有相同的成员等条件。

Am I understanding the stantard correctly?

你对“特殊保证”的引用在6.5.2.3条中的作用在很大程度上被草案N1570的脚注95所削弱,其说明是:

If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called “type punning”)…

注释不是标准的规范性部分,但它引导我们理解标准,而这条注释所适用的文本“一个联合成员表达式的值,例如 x.id 的值,是所命名成员的值”通常被解释为:你可以读取一个联合成员的值,即使它不是最近写入的那个,C实现也会在新类型中重新解释联合的字节。

也就是说,你仍然需要解决确保你在联合中使用的各种 int idCurrentID_tVoltageId_t 使用相同字节的问题。在C 2024中,这一点更容易实现,因为你可以为枚举指定底层类型,如下所示:

typedef enum : int { CURRENT_ID_1, CURRENT_ID_2 } CurrentId_t;

这除了保证枚举具有相同的表示之外,还允许按照C 2024 6.5.1的别名规则,将它们与底层类型进行别名化,为通过联合访问它们提供了额外的理由,至少对于 int 成员是如此。

作为一个语言法学的问题,还有一些额外的复杂性,因为你很可能不是直接访问成员 id,而是通过联合中的一个结构成员来访问它。也就是说,给定某个 Measurement_t x,你可能会引用 x.common.id。因此,问题不仅在于你是否能够访问那个 id 成员,而是在最近写入联合的成员是 x.currentx.voltage 时,是否可以先访问 x.common。同样地,这些也不是兼容类型,因此“不适用这条特殊保证”,但将联合的字节重新解释为新类型的规则仍然适用。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章