Какой смысл разрешать идентификатор для перечисления?

Почему компилятор не жалуется, когда я пытаюсь назначить неверные значения переменной a типа enum answer?

#include <stdio.h>

int main()
{
    enum answer {NO, YES};
    enum gender {MALE, FEMALE};

    enum answer a = 5; /* Assign an invalid value. */
    printf("answer: %d\n", a);

    a = MALE; /* Assign a value of wrong type */
    printf("answer: %d\n", a);

    return 0;
}

Вот результат:

$ gcc -std=c99 -pedantic -Wall -Wextra enum.c 
$ ./a.out
answer: 5
answer: 0

Если enum не приводит к проверке типов, то в чем смысл синтаксиса:

enum [identifier] {enumerator-list}

Я использовал answer и gender как идентификатор моего перечисления. Какой смысл разрешать этот синтаксис?

Я имею в виду, что этот код может быть очень хорошо написан как

enum {NO, YES};
enum {MALE, FEMALE};

В чем смысл этого синтаксиса?

enum answer {NO, YES};
enum gender {MALE, FEMALE};

Ответ 1

Почему компилятор не жалуется, когда я пытаюсь назначить неверные значения переменной a типа enum answer?

Так как в C an enum практически эквивалентен int. Это было стандартизировано, как и это, и просто слишком много программ полагаются на это поведение, чтобы изменить его.

В С++ они разные типы, и компилятор жалуется:

$ g++ -Wall -Wextra a.c
a.c: In function 'int main()':
a.c:8:24: error: invalid conversion from 'int' to 'main()::answer' [-fpermissive]
a.c:11:14: error: cannot convert 'main()::gender' to 'main()::answer' in assignment

В чем смысл этого синтаксиса?

Мое быстрое предположение было бы совместимостью вперед.

Ответ 2

C предоставляет значения enumeration непосредственно как integer, а в С++ enum - реальный тип. Следовательно, в С++ enum выполняется проверка типа , а в C перечисление только представляет константы типа int. Поэтому значения integer и enum могут быть перемешаны во всех арифметических операциях.

Ответ 3

Предполагая, что:

enum answer {NO, YES};

enum gender {MALE, FEMALE};

с:

enum answer bla = YES; 
enum gender blop = MALE;
int bip = 0;

Хотя это не требуется стандартом C, он позволяет предупреждениям в следующих случаях:

bla = blop;

bip =  bla;

Обратите внимание, что типы enum являются арифметическими типами, и назначение между объектами разных арифметических типов всегда разрешено, но реализация по-прежнему может быть предупреждена. Как отмечалось в комментариях, правила с типами enum отличаются для С++.

EDIT: еще один пример:

enum ans {
    YES,
    NO,
    MAYBE
};


enum ans a = /* initializer */;

switch (a)
{
    case YES:
        break;
    case NO:
        break;
}

С -Wall (то есть -Wswitch), gcc может предупредить, что:

tst.c: 15: 5: предупреждение: значение перечисления "MAYBE не обрабатывается в коммутаторе

Если у вас есть:

enum {
    YES,
    NO,
    MAYBE
};

int a = /* initializer */;

gcc не сможет предупредить. Так что фактически использование типа enum вместо константы enum помогает статическим анализаторам.