Почему две переменные перечисления перечисления имеют одно и то же целочисленное значение?

Я знаю, что если бы я определил день недели перечисления следующим образом:

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

Затем MON по умолчанию будет 0, а TUE - 1, WED - 2...

Но если я определяю его так:

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

Тогда оба MON и TUE получат значение 0.

Как система будет дифференцировать MON и TUE внутри? Я имею в виду, если я объявлю что-то вроде этого:

enum weekday today = 0;

Тогда есть сегодня MON или TUE? Или, философски говоря, оба?

Ответ 1

C перечисления являются "действительно" целыми числами - не только потому, что они реализуются таким образом, а потому, что стандарт определяет типы перечислений, чтобы иметь целочисленные значения. Таким образом, значение today "действительно" 0. Все, что произошло, это то, что вы создали два разных имени для значения 0.

Я полагаю, что ответ "сегодня MON или TUE" - "да"; -)

Язык не останавливает вас, потому что иногда полезно для перечисления иметь несколько имен для одного и того же значения. Например:

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

Ответ 2

Почему две разные константы перечисления имеют одно и то же целочисленное значение?

Поскольку это явно разрешено стандартным проектом N1265 C99 в 6.7.2.2/3 "Спецификаторы перечисления":

Использование счетчиков с = может приводить к константам перечисления со значениями, которые дублируют другие значения в том же перечислении.

Как система будет дифференцировать MON и TUE внутри?

Я думаю, что это невозможно, потому что они являются константами времени компиляции (6.6/6 "Константные выражения" ). В результате они:

  • не могут быть изменены, чтобы они отличались после компиляции

  • не имеют адреса, чтобы рассказать им обособленно: Место хранения значения перечисления в 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) { }

Ответ 4

Это как философское (или нет) как

#define ZILCH 0
#define NADA  0

Существует много применений, когда имеет смысл иметь разные имена в том же количестве.

Ответ 5

Имя константы перечисления используется для назначения значения, а не самого фактического значения. Если вы присвоите значение 0 сегодня, выходное значение будет равно 0. И да, как MON, так и TUE будут иметь значение 0, а оставшиеся будут назначены значение WED = 1 THU = 2 и т.д.