我正在使用提供给我的C库。我在编译库时使用的编译器,版本,选项等信息有限。库接口在传递的结构中使用enum
,并直接作为传递的参数。
问题是:当我编译代码以使用提供的库时,我如何保证或确定我的编译器将使用与enum
s相同的大小?如果没有,则结构不会排列,并且参数传递可能会混乱,例如, long
与int
。
我的担忧源于C99标准,该标准指出enum
类型:
应与char,有符号整数类型或无符号整数兼容 整数类型。类型的选择是实现定义的,但应该 能够代表所有成员的价值观 枚举。
据我所知,只要最大值适合,编译器就可以选择任何类型,它非常随意,有效地随心所欲,不仅可能在编译器之间变化,而且可能在同一编译器的不同版本和/或编译器选项。它可以选择1,2,4或8字节表示,从而导致结构和参数传递中的潜在不兼容性。 (它也可以选择签名或未签名,但我没有看到在这种情况下出现问题的机制。)
我在这里遗漏了什么吗?如果我没有遗漏某些内容,这是否意味着永远不应该在API中使用enum
?
更新
是的,我错过了一些东西。虽然语言规范在这里没有帮助,但正如@Barmar所指出的应用程序二进制接口(ABI)所做的那样。或者如果没有,则ABI不足。 ABI for my system确实指定enum
必须是带符号的四字节整数。如果编译器不服从,那就是一个bug。给定完整的ABI和兼容编译器,enum
可以在API中安全使用。
答案 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中使用枚举。
使用“-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开关来定制行为。
使用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