只要把目标数据类型强制设为足够大,并用一个很大的常量来保证,枚举转换就安全吗?

编程语言 2026-07-10

我在用C99编写一个软件,该软件由两个Git仓库组成,其中一个是子模块。

子模块在一个头文件中声明了一个错误代码枚举:

enum err_cause {
    ERR_CAUSE_A = 1,
    ERR_CAUSE_B = 2,
    ERR_CAUSE_LAST = 0x7FFFFFFF
};

然而,父仓库需要添加几个仅与父仓库相关的错误代码。它添加了:

enum {
    ERR_CAUSE_PARENT_A = 0x1001,
    ERR_CAUSE_PARENT_B = 0x1002
};

我之所以在 ERR_CAUSE_LAST 上使用一个很大的值,是因为我想防止编译器认定该枚举中所有值都适合放入一个8 位字段,这可能会使 enum err_cause 变成 int8_tuint8_t

现在,在已经定义了枚举和无标签枚举常量之后,我可以这样做吗:

enum err_cause err = ERR_CAUSE_PARENT_A;

我假设任何合理的实现都会允许它,但我也想为不合理的实现做好准备。或者我必须把 err_cause_t 定义为一个typedef,使其成为 uint32_t

代码所需的具体C 标准版本是C99。当然,在更新的C 标准下也能很好地编译。C89太过陈旧,缺少我需要的全部特性。

解决方案

C 1999 6.7.2.2 4指出:

每个枚举类型应与 char、一种带符号整数类型,或与无符号整数类型兼容。类型的选择由实现定义,但应能够表示该枚举所有成员的值。

标准并未明确具体地定义“兼容”到底意味着什么,但6.5 7的别名规则告诉我们,一个对象的类型可以通过一个兼容的类型来访问。这显然是为了产生直观上预期的行为。也就是说,不会出现把位模式解释成不同值的异常情况。(尽管我们必须注意,这些规则允许通过相应的无符号类型访问有符号类型,反之亦然,对于在有符号类型中符号位设定的位模式,这将产生不同的值。)

因此,一个枚举类型的对象在本质上就是某种整数类型,因此你可以把它赋予该整数类型的任何值,并期望该整数类型的正常行为。

严格来说,enum err_cause err = ERR_CAUSE_PARENT_A; 包含从 ERR_CAUSE_PARENT_A(它是一个 int)到类型 enum err_cause 的转换:

  • ERR_CAUSE_PARENT_A 是一个 int,因为6.4.4.3 2指出,被声明为枚举常量的标识符的类型是 int
  • 这种初始化的规则,在C 1999 6.7.8 11中,说的是使用简单赋值的规则。
  • 简单赋值的规则,在6.5.16.1 2中,指出右操作数的值被转换为左操作数的类型。

在这种转换中会发生什么?6.3.1.3 1指出:

当一个具有整数类型的值被转换为除了 _Bool 之外的另一种整数类型时,如果该值可以被新类型表示,则保持不变。

我们知道 ERR_CAUSE_PARENT_A 可以在 enum err_cause 中表示。这是因为每个整数类型可表示的值是连续的(没有间隙)。ERR_CAUSE_PARENT_A 位于在 enum err_cause 中可表示的两个值之间,因此处于与 enum err_cause 兼容的整数类型可表示的两个值之间,因此是一个在 T 可表示的值,因此在 enum err_cause 也可表示。

鉴于 ERR_CAUSE_PARENT_A 的值可以被与之兼容的任意整数类型(enum err_cause 兼容的类型)表示,我们应当期望它保持不变。

C 2024 6.7.3.3明确地说明了这一点:

… 转换为枚举类型具有与转换为底层类型相同的语义。

回到C 1999,我们来考虑另一种情况。如果把 int 转换为枚举类型不遵守转换到整数类型的规则,那么在简单赋值 err = ERR_CAUSE_A 中会发生什么?如果转换到整数类型不适用,那么在转换章节(6.3)中就没有关于把 ERR_CAUSE_A(它是一个 int)转换到 err(它是一个 enum err_cause)的规则。当标准对某个操作无规定时,意味着其行为是未定义的。

这意味着标准对行为没有任何要求。实现可以在 err = ERR_CAUSE_A 处触发异常,或赋予一个意外的值,甚至让程序完全失控,覆盖内存、调用任意例程,并向所有流写入垃圾数据。

换言之,如果把一个在兼容类型中可表示的值赋给一个枚举类型的对象,其赋值不受将其转换为该兼容类型的规则约束,那么C 程序就不能依赖把枚举的实际成员赋给该枚举类型的对象。这显然不是本来打算的。因此,我们可以得出结论,应该由转换为兼容整数类型的规则来支配该转换。

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

相关文章