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

时间:2013-12-28 00:25:29

标签: c struct enums linker portability

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

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

我的担忧源于C99标准,该标准指出enum类型:

  

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

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

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

更新

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

2 个答案:

答案 0 :(得分:9)

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

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

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

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

答案 1 :(得分:2)

从问题:

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

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

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

#include <stdio.h>

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