将无效值与枚举进行比较

3
我想尝试理解编译器在将枚举与无效值进行比较时的工作原理,以及程序在执行期间正在做什么。
我在工作中找到了奇怪的源代码,但不理解程序的行为,因为它没有给出我预期的结果。
我编写了下面的小程序来总结我的问题。
我创建了一个枚举类型E_Number,并用值-1实例化一个变量a。然后我对a执行比较,以检查它是否属于枚举范围。
(我知道,这真的很奇怪,但这正是我在源代码中发现的!)
我希望结果告诉我“不在范围内”,因为第一个条件(a >= FIRST_ENUM)失败了。但是它是第二个条件(a < NB_MAX_NUMBER)失败了,给了我正确的结果(请参见printf())...
如果我在if条件中将a强制转换为(int),我会得到预期的结果。
那么在执行期间会发生什么?程序是否将-1视为另一个可能的枚举值,该值将定位在NB_MAX_NUMBER之后?关于枚举的>和<运算符规则是什么?
#include <stdio.h>

#define FIRST_ENUM 0
typedef enum{
    NUM_1 = FIRST_ENUM, 
    NUM_2, 
    NUM_3,
    NB_MAX_NUMBER
}E_Number; 

int main()
{
    E_Number a = -1; 

    if ((a >= FIRST_ENUM) && (a < NB_MAX_NUMBER))
    {
        printf("In Range\n"); 
    }
    else
    {
        printf("Not in Range\n"); 
    }

    printf("1st condition = %s\n", (a >= FIRST_ENUM)?"TRUE":"FALSE"); 
    printf("2nd condition = %s\n", (a < NB_MAX_NUMBER)?"TRUE":"FALSE"); 

    return 0; 
}

``` 使用gcc编译程序: gcc program.c 运行程序: .\a.exe 输出结果: 不在范围内 第一个条件为真 第二个条件为假 我正在使用MINGW编译器(gcc (x86_64-win32-seh-rev1, Built by MinGW-W64 project) 4.9.2) ```

1
有趣的是,我无法重现结果(MSVC),最后两个分别为FALSE和TRUE,这正是我所期望的。 - Weather Vane
1
你确定这里展示的程序是你编译并执行的程序吗? - Mahesh
感谢回答;我不知道“枚举”可以是“无符号的”。 - Weather Vane
1
@eugene-sh 我理解你的评论!这段代码存在于航空软件中,当一个函数“接收”枚举值时,不能确定结果是否有效并属于枚举范围... - GOoOGle
我在几个月前遇到这个问题时得出的一个结论是,永远不要再使用“枚举类型”,而是在存储或“返回”枚举时始终使用“int”。这样我就可以确切地知道(好吧,不是完全确切,因为“int”不是固定宽度,但尽可能确切)变量的类型。至少我可以保证我不会找到任何“unsigned”变量。 - alx - recommends codidact
显示剩余4条评论
3个回答

2
在您的情况下,编译器将E_Number视为unsigned int,因为所有合法值都是无符号的,因此-1被认为是~0u,它大于等于FIRST_ENUM并且小于NB_MAX_NUMBER。
我在使用gcc version 6.3.0 20170516 (Raspbian 6.3.0-18+rpi1+deb9u1)时也遇到了同样的问题。
pi@raspberrypi:~ $ ./a.out 
Not in Range
1st condition = TRUE
2nd condition = FALSE

"最初的回答",但是如果我将你的定义更改为以下内容:

但是,如果我像这样更改您的定义:

#include <stdio.h>

#define FIRST_ENUM -1
typedef enum{
    NUM_1 = FIRST_ENUM, 
    NUM_2, 
    NUM_3,
    NB_MAX_NUMBER
}E_Number; 

int main()
{
    E_Number a = -2; 

    if ((a >= FIRST_ENUM) && (a < NB_MAX_NUMBER))
    {
        printf("In Range\n"); 
    }
    else
    {
        printf("Not in Range\n"); 
    }

    printf("1st condition = %s\n", (a >= FIRST_ENUM)?"TRUE":"FALSE"); 
    printf("2nd condition = %s\n", (a < NB_MAX_NUMBER)?"TRUE":"FALSE"); 

    return 0; 
}

这里涉及到行为改变和枚举类型被视为整数,我有:

最初的回答:

pi@raspberrypi:~ $ ./a.out 
Not in Range
1st condition = FALSE
2nd condition = TRUE

1

枚举常量是 类型 int 的。枚举类型是未指定的整数类型,能够表示所有枚举常量。

6.7.2.2p4:

每个枚举类型都应与char、有符号整数类型或无符号整数类型兼容。类型的选择是实现定义的,但必须能够表示枚举成员的所有值。枚举类型在枚举器声明列表终止的}之后立即变为不完整,并在此之后变为完整。
由于您没有枚举任何负值,因此该类型很可能是无符号类型。如果是这样,则(E_Number)some_integer将始终大于或等于零(0==FIRST_ENUM)。
如果将enum列表扩展到:
typedef enum{
    NUM_NOPE=-1,
    NUM_1 = FIRST_ENUM, 
    NUM_2, 
    NUM_3,
    NB_MAX_NUMBER
}E_Number; 

你将强制编译器使用有符号类型,结果会反转。


0

来自ISO/IEC 9899:1999,6.7.2.2p3的引用:

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

因此,当您声明一个枚举时,您不能事先确定C的实现将选择存储哪种数据类型的变量。为了优化原因,如果您将枚举常量存储在[-128,+127]之间,则编译器可能不会选择4字节的整数类型。实现可以选择char来存储枚举变量,但您不能确定。只要它可以存储所有可能的值,任何整数数据类型都可以被选择。


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