枚举是否永远不会在API中使用?

我正在使用提供给我的C库。 我在编译库时使用的编译器,版本,选项等信息有限。 库接口在传递的结构中使用enum ,并直接作为传递的参数使用。

问题是:当我编译代码以使用提供的库时,我如何保证或确定我的编译器将使用相同大小的enum ? 如果没有,则结构将不会排列,并且参数传递可能会混乱,例如longint

我的担忧源于C99标准,该标准规定了enum类型:

应与char,有符号整数类型或无符号整数类型兼容。 类型的选择是实现定义的,但应能够表示枚举的所有成员的值。

据我所知,只要最大值适合,编译器就可以选择任何类型,它非常随意,有效地随心所欲,不仅可能在编译器之间变化,而且可能在同一编译器和/或编译器选项的不同版本之间变化。 它可以选择1,2,4或8字节表示,从而导致结构和参数传递中的潜在不兼容性。 (它也可以选择签名或未签名,但我没有看到在这种情况下出现问题的机制。)

我在这里错过了什么吗? 如果我没有遗漏某些内容,这是否意味着永远不应该在API中使用enum

更新:

是的,我错过了什么。 虽然语言规范在这里没有帮助,但正如@Barmar所指出的应用程序二进制接口(ABI)所做的那样。 或者如果没有,则ABI不足。 我系统的ABI确实指定enum必须是带符号的四字节整数。 如果编译器不服从,那就是一个bug。 给定完整的ABI和兼容编译器, enum 可以在API中安全使用。

使用枚举的API取决于编译器将保持一致的假设,即给定相同的枚举声明,它将始终选择相同的基础类型。

虽然语言标准并没有特别要求这样做,但编译器做其他任何事情都是相当不正常的。

此外,特定操作系统的所有编译器都需要与操作系统的ABI保持一致。 否则,您将遇到更多问题,例如使用64位int的库,而调用者使用32位int 。 理想情况下,ABI应该约束enum的表示,以确保兼容性。

更一般地,语言规范仅确保使用相同实现编译的程序之间的兼容性。 ABI确保使用不同实现编译的程序之间的兼容性。

从问题:

我系统的ABI确实指定枚举必须是带符号的四字节整数。 如果编译器不服从,那就是一个bug。

我很惊讶。 我怀疑实际上,如果你定义一个枚举常量,其值大于2 ^ 32,那么编译器将为你的枚举选择一个64位(8字节)的大小。

在我的平台上(针对x86和gcc 4的MinGW gcc 4.6.2,针对x86_64的Linux上的.4),以下代码表明我得到4和8字节的枚举:

 #include  enum { a } foo; enum { b = 0x123456789 } bar; int main(void) { printf("%lu\n", sizeof(foo)); printf("%lu", sizeof(bar)); return 0; } 

我用-Wall -std=c99开关编译。

我想你可以说这是一个编译器错误。 但是删除对大于2 ^ 32的枚举常量的支持或者总是使用8字节枚举的替代方案似乎都是不合需要的。

鉴于GCC的这些常见版本不提供固定大小的枚举,我认为唯一安全的操作通常是不在API中使用枚举。

GCC的进一步说明

使用“-pedantic”进行编译会导致生成以下警告:

 main.c:4:8: warning: integer constant is too large for 'long' type [-Wlong-long] main.c:4:12: warning: ISO C restricts enumerator values to range of 'int' [-pedantic] 

可以通过–short-enums和–no-short-enums开关来定制行为。

Visual Studio的结果

使用VS 2008 x86编译上述代码会导致以下警告:

 warning C4341: 'b' : signed value is out of range for enum constant warning C4309: 'initializing' : truncation of constant value 

使用VS 2013 x86和x64,只需:

 warning C4309: 'initializing' : truncation of constant value