【问题标题】:Why can two different enum enumeration-constants have the same integer value?为什么两个不同的枚举枚举常量可以具有相同的整数值?
【发布时间】:2012-07-10 11:37:37
【问题描述】:

我知道如果我这样定义一个枚举工作日:

enum weekday {
    MON,
    TUE,
    WED,
    THU,
    FRI,
};

然后,MON 在内部默认为 0,TUE 为 1,WED 为 2...

但如果我这样定义:

enum weekday {
    MON,
    TUE = 0,
    WED,
    THU,
    FRI,
};

那么MONTUE 的值都是0。

系统如何在内部区分 MON 和 TUE?我的意思是,如果我声明这样的内容:

enum weekday today = 0;

那么今天是MON 还是TUE?或者,从哲学上讲,两者兼而有之?

【问题讨论】:

  • @quasiverse - 我做到了,MONTUE 都是 0。 +1,我不知道。
  • enum 常量是 ints。它们只是希望对某些整数常量有意义的名称。那么,如果你可以用两个名字来指代同一个东西,那又有什么关系呢?
  • 没有哲学,只有逻辑:today == MON == TUE == 0.
  • 对于不支持 constexpr 的系统,它基本上是 constexpr 的替代品。除了 constexpr 整数是耦合的

标签: c enums


【解决方案1】:

C 枚举是“真正的”整数——不仅因为它们碰巧以这种方式实现,还因为标准的定义枚举类型具有整数值。所以today 的值是“真的” 0。所发生的只是你为值 0 创建了两个不同的名称。

我想“今天是周一还是周二”的答案是“是”;-)

语言不会阻止您,因为有时枚举具有多个名称来表示相同的值很有用。例如:

enum compression_method {
    COMP_NONE = 0,
    COMP_LOW = 1,
    COMP_HIGH = 2,
    COMP_BEST = 2,
    COMP_FASTEST = 0,
};

【讨论】:

  • 哈哈哈,+1 I suppose then that the answer to "is today MON or TUE" is "yes" ;-):D
  • 在我看来,更好的解决方案是使用COMP_FASTEST = COMP_NONE
  • 即使 MON 和 TUE 不同,“今天是 MON 还是 TUE”的答案仍然可以是“是”,给定计算机的“OR”运算。我想你在搜索“今天是周一和周二”
【解决方案2】:

为什么两个不同的枚举常量可以有相同的整数值?

因为 N1265 C99 standard draft 在 6.7.2.2/3 “枚举说明符”中明确允许它:

将枚举数与= 一起使用可能会产生枚举常量,其值与同一枚举中的其他值重复。

系统如何在内部区分 MON 和 TUE?

我认为这是不可能的,因为它们是编译时常量(6.6/6“常量表达式”)。因此他们:

  • 编译后无法修改使它们不同

  • 没有地址可以区分他们:Memory location of enum value in C

    编译时常量不需要任何地址,因为地址对于你无法修改的东西是无用的。

GCC 只是在编译时将枚举成员的使用替换为汇编中的立即值。考虑:

#include <stdio.h>

enum E {
    E0 = 0x1234,
    E1 = 0x1234
};
int i = 0x5678;

int main() {
    printf("%d\n", E0);
    printf("%d\n", E1);
    printf("%d\n", i);
    return 0;
}

使用 GCC 4.8 x86_64 编译和反编译:

gcc -c -g -O0 -std=c89 main.c
objdump -Sr main.o

输出包含:

    printf("%d\n", E0);
   4:       be 34 12 00 00          mov    $0x1234,%esi
  ...
    printf("%d\n", E1);
  18:       be 34 12 00 00          mov    $0x1234,%esi
  ...
    printf("%d\n", i);
  2c:       8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 32 <main+0x32>
                    2e: R_X86_64_PC32       i-0x4
  32:       89 c6                   mov    %eax,%esi

所以我们看到:

  • 枚举成员被用作直接$0x1234,因此无法知道它们来自哪里
  • 变量i然而来自内存0x0(%rip)(将被重新定位),所以两个变量可以通过地址来区分

【讨论】:

    【解决方案3】:

    为了补充其他答案,我将举一个实际示例,说明如何在给定的 enum 上对不同枚举使用相同的值非常有用:

    enum slots_t {
        SLOT_FIRST = 0,
        SLOT_LEFTARM = SLOT_FIRST,
        SLOT_RIGHTARM = 1,
        SLOT_TORSO = 2,
        SLOT_LEFTLEG = 3,
        SLOT_RIGHTLEG = 4,
        SLOT_LAST = SLOT_RIGHTLEG
    };
    

    然后你可以在你的代码中做:

    for (int i = SLOT_FIRST; i <= SLOT_LAST; ++i) { }
    

    【讨论】:

    • 我宁愿定义一个 SLOT_MAX = 4 而不是 SLOTLAST = SLOT_RIGHTLEG,以防其他程序员想到交换 SLOT_LEFTLEG 和 SLOT_RIGHTLEG 的值的好主意
    • 通常我使用没有强制值的 SLOT_MAX 让编译器分配最后一个 + 1。这允许定义数组,例如:bool slot_valid[SLOT_MAX]; 或使用更传统的 i &lt; SLOT_MAX 循环。
    【解决方案4】:

    它和哲学(或不是)一样

    #define ZILCH 0
    #define NADA  0
    

    在许多用途中,不同的名称产生相同的数字是有意义的。

    【讨论】:

      【解决方案5】:

      枚举常量的名称用于分配值,而不是实际值本身。如果将值 0 分配给今天,则输出值将为 0。 是的,MON 和 TUE 的值都为 0,其余的将赋值为 WED=1 THU=2,依此类推。

      【讨论】:

        猜你喜欢
        • 2015-08-21
        • 1970-01-01
        • 2016-09-12
        • 2014-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多