将枚举定义转换为无符号整数

9
根据这篇SO帖子:
C语言中枚举类型的大小是多少?
枚举类型具有signed int类型。我想将一个枚举定义从signed int转换为unsigned int

例如,在我的平台上,unsigned int的宽度为32位。我想创建一个枚举:
typedef enum hardware_register_e
{
    REGISTER_STATUS_BIT = (1U << 31U)
} My_Register_Bits_t;

我的编译器提示上述定义超出范围(对于signed int而言确实如此)。

如何声明unsigned intenum值?

编辑1:

  1. 优先选择不扩展到64位(因为代码驻留在嵌入式系统中)。
  2. 由于技能限制,本项目不允许使用C++。:(

编辑2:

  • 编译器是IAR Embedded Workbench for ARM7。

1
清楚明确一点,C++ 是一个选项吗? - Richard J. Ross III
@DanielFischer:这个可移植吗?请提交为答案。 - Thomas Matthews
@DanielFisher:~0x7fffffff 和 -2147483648 一样具有可移植性。它们评估为相同的位模式。我在我的答案中概述的相同注意事项适用。 - Nominal Animal
@NominalAnimal 位补码可以在使用一补码的机器上运行,而-2147483648则不行。因此,它可能更具可移植性。尽管如此,我主要喜欢它是因为它使位模式更加明显。 - Daniel Fischer
@NominalAnimal 我记得最近读到过还有一两个补码系统存在。但实际上,我认为 int 是 64 位的系统将在我们遇到补码机之前达到 -2147483648 的破坏点。 - Daniel Fischer
显示剩余4条评论
4个回答

4
根据这篇SO文章:C语言中的枚举类型大小是由有符号整型决定的。但是在C语言中,enum类型可以是int类型,但它们不是int类型。在gcc中,enum类型默认为unsigned int ,但enum常量是int类型,而enum类型则是由实现定义的。在您的情况下,enum常量是int类型,但您给它的值不适合int类型。在C语言中,无法使用unsigned int enum常量,因为C语言认为它们是int类型。1)gcc实现定义的enum类型文档:“通常,如果枚举中没有负值,则该类型为unsigned int;否则为int”参见 http://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit_002dfields-implementation.html

1
在这个问题的背景下,这意味着什么? - Richard J. Ross III
枚举常量可以声明为无符号的吗? - Thomas Matthews
@ThomasMatthews 不,它们始终是 int 类型。 - ouah
抱歉,我曾经因为认为enum类型是unsigned int与C标准相矛盾而对其进行了负评,但我错了。C99 §6.7.2.2/4指出这是实现定义(只有常量需要是int)。如果您编辑了内容,我会撤销这个负评(由于时间过长,我现在无法撤销)。 - Adam Rosenfield
1
Gcc似乎支持比int更大的enum:请参见https://dev59.com/7WHVa4cB1Zd3GeqPjBC3#9524663。 - Joseph Quinsey
显示剩余2条评论

3

很遗憾,ISO C标准(c99 6.4.4.3)规定枚举常量的类型为int。如果您使用例如gcc -W -std=c89 -pedantic进行编译,它将发出警告ISO C restricts enumerator values to range of ‘int’ [-pedantic]。一些嵌入式编译器可能根本无法接受该代码。

如果您的编译器比较挑剔,您可以通过使用以下方法解决问题:

typedef enum hardware_register_e
{
    REGISTER_STATUS_BIT = -2147483648   /* 1<<31, for 32-bit two's complement integers */
} hardware_register_t;

但是只有在您的架构上,int 是 32 位二进制补码类型时才能正常工作。在我曾经使用或听说过的所有 32 位和 64 位架构上都是如此。
编辑后添加:ARM7 使用 32 位二进制补码 int 类型,所以上述代码应该可以正常工作。我建议您保留注释,解释实际值为 1<<31。您永远不知道是否会有人移植代码或使用另一个编译器。如果新编译器发出警告,则同一行上的注释应使其变得容易修复。个人建议将代码包装在条件语句中,例如:
typedef enum hardware_register_e
{
#ifdef __ICCARM__
    REGISTER_STATUS_BIT = -2147483648   /* 1<<31, for 32-bit two's complement integers */
#else
    REGISTER_STATUS_BIT = 1 << 31
#endif
} hardware_register_t;

IAR枚举类型不同。请参见其帮助中的“--enum-is-int”。 尽管如此,我仍然找不到将枚举设置为无符号类型的方法。 - lkanab
如果您启用了IAR语言扩展(使用“-e”命令行选项),那么您可以使用例如typedef enum hardware_register_e { REGISTER_STATUS_BIT = 0x80000000UL } hardware_register_t;,在这种情况下,枚举将是unsigned long类型。请参阅IAR C/C++ Development Guide for ARM的第169页和第211页。我已经添加了一个更详细的答案到您提出的问题中,我认为您正在提及它。 - Nominal Animal

2

你无法(至少是可移植的)更改枚举类型。标准明确规定枚举常量的类型为int。要消除警告,可以使用强制转换:

typedef enum hardware_register_e
{
    REGISTER_STATUS_BIT = (int)(1U << 31U)
} My_Register_Bits_t;

可能的替代方案

如果这不需要在标题中,您可以使用所需类型的const对象来代替枚举常量:

const unsigned int REGISTER_STATUS_BIT = (1U << 31);

否则,您可以使用宏定义:
#define REGISTER_STATUS_BIT (1U << 31)

仅当在头文件中定义时。 - vitaut
你会在哪里定义枚举? - Richard J. Ross III
这取决于你在哪里使用它,或者你总是在头文件中定义它们?=) - vitaut
1
在C语言中,const并不是定义常量,而是只读对象。 - ouah
在头文件中定义常量对象没有任何问题 - 只需将它们设为static即可。 - Christoph

1

检查一下你的编译器是否有选项或者预处理指令可以将枚举类型设置为无符号。如果没有,那么你只能使用普通的unsigned int(或者像uint32_t这样的固定宽度类型)来代替枚举类型,并使用#define来定义它可以取的值。


1
由于我将在嵌入式医疗设备中使用该代码,所以我希望有比#define更安全的东西。 - Thomas Matthews
我认为IAR没有这样的#pragma或选项。我会向他们的支持部门发送一个问题并更新。 - lkanab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接