Почему это:
#include <stdio.h>
#include <limits.h>
#include <inttypes.h>
int main() {
enum en_e {
en_e_foo,
en_e_bar = UINT64_MAX,
};
enum en_e e = en_e_foo;
printf("%zu\n", sizeof en_e_foo);
printf("%zu\n", sizeof en_e_bar);
printf("%zu\n", sizeof e);
}
печатать 4 8 8
на C и 8 8 8
на C ++ (на платформе с 4-х байтовыми вставками)?
У меня создалось впечатление, что UINT64_MAX
присвоение приведет к тому, что все константы перечисления будут иметь как минимум 64 бита, но en_e_foo
остаются равными 32 в обычном C.
В чем причина несоответствия?
Ответы:
В C
enum
константа имеет типint
. В C ++ это перечислимый тип.enum en_e{ en_e_foo, en_e_bar=UINT64_MAX, };
В C это нарушение ограничения , требующее диагностики ( при
UINT64_MAX
превышенииINT_MAX
, что очень вероятно). Компилятор AC может полностью отклонить программу или вывести предупреждение, а затем сгенерировать исполняемый файл, поведение которого не определено. (Не совсем ясно, что программа, которая нарушает ограничение, обязательно имеет неопределенное поведение, но в этом случае стандарт не говорит, каково это поведение, поэтому это все еще неопределенное поведение.)gcc 6.2 об этом не предупреждает. лязг делает. Это ошибка в gcc; он неправильно подавляет некоторые диагностические сообщения при использовании макросов из стандартных заголовков. Спасибо Гжегожу Шпетковски за обнаружение отчета об ошибке: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613
В C ++ каждый тип перечисления имеет базовый тип , который представляет собой некоторый целочисленный тип (не обязательно
int
). Этот базовый тип должен иметь возможность представлять все постоянные значения. Таким образом, в этом случае оба типаen_e_foo
иen_e_bar
имеютen_e
ширину не менее 64 бита, даже если ониint
уже.источник
UINT64_MAX
чтобы не превышать,INT_MAX
требуетсяint
не менее 65 бит.-Wpedantic
и,18446744073709551615ULL
но не сUINT64_MAX
.int
должен быть подписанный тип, поэтому он должен быть не менее 65 бит, чтобы иметь возможность репрезентацииUINT64_MAX
(2 ** 64-1).en_e_bar
не больше, чем перечисление,en_e_foo
меньше. Переменная enum была больше самой большой константы.Этот код просто недопустим для C в первую очередь.
Раздел 6.7.2.2 как в C99, так и в C11 говорит, что:
Диагностика компилятора является обязательной, поскольку это нарушение ограничения, см. 5.1.1.3:
источник
В C , хотя a
enum
считается отдельным типом, сами счетчики всегда имеют типint
.Таким образом, поведение, которое вы видите, является расширением компилятора.
Я бы сказал, что имеет смысл увеличивать размер одного из счетчиков только в том случае, если его значение слишком велико.
С другой стороны, в C ++ все перечислители имеют тип, в котором
enum
они объявлены.Из-за этого размер каждого счетчика должен быть одинаковым. Таким образом, размер целого
enum
расширен для хранения самого большого счетчика.источник
Как указывали другие, код плохо сформирован (в C) из-за нарушения ограничений.
Существует ошибка GCC № 71613 (о которой сообщалось в июне 2016 г.), в которой говорится, что некоторые полезные предупреждения заглушаются макросами.
Текущий обходной путь может заключаться в добавлении макроса унарным
+
оператором:enum en_e { en_e_foo, en_e_bar = +UINT64_MAX, };
что дает ошибку компиляции на моей машине с GCC 4.9.2:
$ gcc -std=c11 -pedantic-errors -Wall main.c main.c: In function ‘main’: main.c:9:20: error: ISO C restricts enumerator values to range of ‘int’ [-Wpedantic] en_e_bar = +UINT64_MAX
источник
C11 - 6.7.2.2/2
en_e_bar=UINT64_MAX
является нарушением ограничения, и это делает приведенный выше код недействительным. Должно появиться диагностическое сообщение, подтверждающее реализацию, как указано в черновике C11:Похоже, что в GCC есть ошибка, и ему не удалось создать диагностическое сообщение. (Bug указывается в ответе по Гжегож Szpetkowski
источник
sizeof
является оператором времени компиляции. Здесь нет UB, и даже если бы он был, это не могло бы повлиятьsizeof
.short s = 0xdeadbeef
), и поведение определяется реализацией.Я взглянул на стандарты, и моя программа, похоже, нарушает ограничения в C из-за 6.7.2.2p2 :
и определен в C ++ из-за 7.2.5:
источник