Перечислимые константы ведут себя по-разному в C и C++



почему это:



#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("%zun", sizeof en_e_foo);
printf("%zun", sizeof en_e_bar);
printf("%zun", sizeof e);
}


print 4 8 8 в C и 8 8 8 в C++ (на платформе с 4 байтами ints)?



у меня сложилось впечатление, что UINT64_MAX назначение заставило бы все константы перечислений по крайней мере 64 бит, но en_e_foo остается на 32 в простой C.



каково обоснование этого несоответствия?

470   6  
c

6 ответов:

в C, an enum константа имеет тип int. В C++ он имеет перечисляемый тип.

enum en_e{
    en_e_foo,
    en_e_bar=UINT64_MAX,
};

в C, это нарушение ограничения, требующие диагностики (еслиUINT64_MAX превышает INT_MAX, что это очень вероятно). Компилятор C может полностью отклонить программу или напечатать предупреждение, а затем создать исполняемый файл, поведение которого не определено. (Это не 100% ясно, что программа, которая нарушает ограничение обязательно неопределенное поведение, но в этом случае стандарт не говорит, что такое поведение, так что это все еще неопределенное поведение.)

gcc 6.2 не предупреждает об этом. лязг делает. Это ошибка в gcc; он неправильно блокирует некоторые диагностические сообщения при использовании макросов из стандартных заголовков. Спасибо Гжегожу Шпетковски за обнаружение сообщения об ошибке:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613

в C++, каждый тип перечисления имеет базовый тип, который является некоторым типом integer (не обязательно int). Этот базовый тип должен быть способен представлять все постоянные значения. Так что в данном случае оба en_e_foo и en_e_bar типа en_e, который должен быть не менее 64 бит, даже если int меньше.

этот код просто не является допустимым C в первую очередь.

раздел 6.7.2.2 в обоих C99 и C11 говорит, что:

ограничения:

выражение, определяющее значение константы перечисления, должно быть целочисленным константным выражением, имеющим значение, представимое как int.

диагностика компилятора является обязательной, поскольку это нарушение ограничений, см. 5.1.1.3:

A соответствующая реализация должна выдавать по меньшей мере одно диагностическое сообщение (идентифицированное определенным реализацией способом), если блок предварительной обработки перевода или блок перевода содержит нарушение любого синтаксического правила или ограничения, даже если поведение также явно указано как неопределенное или определенное реализацией.

на C, в то время как A enum считается отдельным типом, сами перечислители всегда имеют тип int.

C11 - 6.7.2.2 спецификаторы перечисления

3 идентификаторы в списке перечислителей объявляются как константы, имеющие тип 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

выражение, определяющее значение константы перечисления, должно быть целочисленным константным выражением, имеющим значение, представимое как int.

en_e_bar=UINT64_MAX является нарушением ограничений, и это делает приведенный выше код недействительным. Диагностическое сообщение должно быть получено путем подтверждения реализации, как указано в проекте C11:

соответствующая реализация должна производить по меньшей мере одну диагностику сообщение (идентифицированное определенным способом реализации) если блок предварительной обработки перевода или блок перевода содержит нарушение какого-либо синтаксического правила или ограничения, [...]

похоже, что GCC имеет некоторую ошибку, и она не смогла создать диагностическое сообщение. (Ошибка указана в ответ by Гжегож Szpetkowski

Я взглянул на стандарты, и моя программа, похоже, является нарушением ограничений в C из-за 6.7.2.2p2:

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

и определяется в C++ из-за 7.2.5:

Если базовый тип не является фиксированным, тип каждого перечислителя является тип его инициализирующего значения: - Если указан инициализатор для перечислителя инициализирующее значение имеет тот же тип, что и выражение и константа-выражение должны быть интегральной константой выражение (5.19). - Если для первого не указан инициализатор перечислитель, инициализирующее значение имеет неопределенный интегральный тип. - В противном случае тип инициализирующего значения совпадает с типом инициализирующего значения предыдущего перечислителя, если увеличенное значение не представляется в этом типе, и в этом случае тип-это неопределенный интегральный тип, достаточный для содержания увеличенное значение. Если такой тип не существует, программа плохо сформирована.

Comments

    Ничего не найдено.