Подтвердить что ты не робот

Проверка типа перечисления в C/gcc

См. простой пример ниже. Когда функция, возвращающая один enum, присваивается переменной другого enum, я не получаю никаких предупреждений даже с gcc -Wall -pedantic. Почему компилятор C не может выполнять проверку типов на enum s? Или это gcc конкретный? У меня нет доступа к любому другому компилятору прямо сейчас, чтобы попробовать его.

enum fruit {
APPLE,
ORANGE
};

enum color {
RED,
GREEN
};

static inline enum color get_color() {
    return RED;
}

int main() {
    enum fruit ftype;
    ftype = get_color();
}
4b9b3361

Ответ 1

Это объявление:

enum fruit {
    apple,
    orange
};

объявляет три вещи: тип, называемый enum fruit, и два счетчика, называемых apple и orange.

enum fruit на самом деле является отдельным типом. Он совместим с целым типом, определенным для реализации; например, enum fruit может быть совместимо с int, с char или даже с unsigned long long, если реализация выбирает, пока выбранный тип может представлять все значения.

Перечислители, с другой стороны, являются константами типа int. На самом деле существует общий прием использования объявленного enum объявления для объявления констант int без использования препроцессора:

enum { MAX = 1000 };

Да, это означает, что константа apple, даже если она была объявлена ​​как часть определения enum fruit, на самом деле не имеет типа enum fruit. Причины этого исторические. И да, вероятно, было бы больше смысла для счетчиков быть константами типа.

На практике это несоответствие редко имеет значение. В большинстве случаев дискретные типы (т.е. Целые и перечисляемые типы) в значительной степени взаимозаменяемы, и неявные преобразования обычно делают правильные вещи.

enum fruit { apple, orange };
enum fruit obj;      /* obj is of type enum fruit */
obj = orange;        /* orange is of type int; it's
                        implicitly converted to enum fruit */
if (obj == orange) { /* operands are converted to a common type */
    /* ... */
}

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

Один из способов получить сильную проверку типов - это обернуть ваши данные в структуру:

enum fruit { /* ... */ };
enum color { /* ... */ };
struct fruit { enum fruit f; };
struct color { enum color c; };

struct fruit и struct color являются четкими и несовместимыми типами без имплицитного (или явного) преобразования между ними. Недостатком является то, что вы должны явно ссылаться на элемент .f или .c. (Большинство программистов на C просто рассчитывают на то, что они смогут правильно разобраться - со смешанными результатами.)

(typedef не дает вам надежной проверки типа, несмотря на имя, он создает псевдоним для существующего типа, а не новый тип.)

(Правила в С++ немного отличаются.)

Ответ 2

Вероятно, большинство из нас понимают основные причины ( "спецификация говорит, что она должна работать" ), но мы также согласны с тем, что это является причиной множества ошибок программирования на "C" и что обходной обходной путь структуры является валовым, Игнорирование дополнительных шашек, таких как lint, вот что мы имеем:

gcc (4.9): No warning available.
microsoft cl (18.0): No warning available.
clang (3.5): YES -Wenum-conversion

Ответ 3

gcc решил не предупреждать (как и clang), но icc (компилятор Intel) будет предупреждать в этой ситуации. Если вам нужна дополнительная проверка типов для типов enum, вы можете передать свой код некоторому статическому программному обеспечению проверки кода, например Lint, которое может предупреждать в таких случаях.

gcc решил, что было не полезно предупреждать о неявных преобразованиях между типами enum, но также обратите внимание, что C не требует реализации для выдачи диагностики в случае назначения между двумя разными типами enum. Это то же самое, что и для назначения между любым арифметическим типом: диагностика не требуется C. Например, gcc также не будет предупреждать, если вы присвойте long long a char или short a long.

Ответ 4

Это потому, что enum в C - это просто группа уникальных целочисленных констант, что избавляет вас от необходимости иметь #define целую кучу констант. Это не похоже на С++, где создаваемый enum имеет определенный тип. Это как раз то, как C.

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

Ответ 5

Перечисление в C в основном обрабатывается как целое число. Это просто лучший способ использовать константы.

  // this would work as well
  ftype = 1;

Вы также можете указать значения:

  enum color {
     RED=0,GREEN,BLUE
  } mycolor;

  mycolor = 1; // GREEN