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

Существуют ли какие-либо гарантии относительно представления больших значений enum?

Предположим, что у меня (на 32-битной машине)

enum foo {
    val1 = 0x7FFFFFFF, // originally '2^31 - 1'
    val2,
    val3 = 0xFFFFFFFF, // originally '2^32 - 1'
    val4,
    val5
};

каково значение val2, val4 и val5? Я знаю, что могу проверить его, но результат стандартизирован?

4b9b3361

Ответ 1

Из стандарта С++ 11 (§7.2.6):

Для перечисления, базовый тип которого не фиксирован, базовый тип является интегральным типом, который может представлять все значения перечисления, определенные в перечислении. Если интегральный тип не может представлять все значения счетчика, перечисление плохо сформировано. Определяется реализация, который является интегральным типом в качестве базового типа, за исключением того, что базовый тип не должен быть больше, чем int, если значение перечислителя не может быть помещено в int или unsigned int.

Итак, компилятор с радостью сделает The Right Thing, если есть интегральный тип размером более 32 бит. Если нет, то перечисление будет неформальным. Не будет обертывания.

Значения будут следующими:

enum foo {
    val1 =                       0x7FFFFFFF, 
    val2,              //        0x80000000   = 2^31
    val3 =                       0xFFFFFFFF, 
    val4,              //0x0000000100000000   = 2^32
    val5               //0x0000000100000001   = 2^32+1
};

Все возрастающие числа также хорошо определены (§7.2,2):

[...] Определение перечислителя без инициализатора дает перечислителю значение, полученное путем увеличения значения предыдущего перечислителя на единицу.

Ответ 2

В стандарте C:

C11 (n1570), § 6.7.2.2 Спецификаторы перечисления

Каждый перечисленный тип должен быть совместим с char, целочисленным типом со знаком или беззнаковым целым типом. Выбор типа определяется реализацией, но должен быть способен отображать значения всех членов перечисления.

Если базовый тип, используемый компилятором, не способен представлять эти значения, поведение undefined.

C11 (n1570), § 4. Соответствие

Если a '' should или '' не требует требования, которое появляется за пределами ограничения или ограничения времени исполнения, поведение undefined.

Ответ 3

C99/C11

Прелюдия:

5.2.4.2.1 требует int шириной не менее 16 бит; AFAIK нет верхней границы (long должно быть длиннее или равным, хотя, 6.2.5/8).

6.5/5:

Если при оценке выражения возникает исключительное условие (т.е. если результат не определен математически или не входит в диапазон представляемых значений для его типа), то поведение undefined.


Если ваш `int` имеет ширину 32 бит (или меньше)

то пример в OP является нарушением ограничения 6.7.2.2/2:

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

Кроме того, перечисления определяются как константа типа int, 6.7.2.2/3:

Идентификаторы в списке перечислений объявляются как константы с типом int и могут появляться везде, где это разрешено.


Обратите внимание, что существует разница между типом перечисления и типом константы перечисления/перечисления:

enum foo { val0 };
enum foo myVariable;        // myVariable has the type of the enumeration
uint_least8_t v = val0*'c'; // if val0 appears in any expression, it has type int


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

enum foo { val1 = 1, val2 = 5 };
enum foo myVariable = val1;    // allowed to be 8-bit

Но, похоже, это запрещает расширение, например

enum foo { val1 = INT_MAX+1 }; // constraint violation AND undefined behaviour
// not sure about the following, we're already in UB-land
enum foo myVariable = val1;    // maximum value of an enumerator still is INT_MAX
                               // therefore myVariable will have sizeof int

Автоинкремент счетчиков

Из-за 6.7.2.2/3,

[...] Каждый последующий счетчик без = определяет его константу перечисления как значение выражения константы, полученного добавлением 1 к значению предыдущей константы перечисления. [...]

в результате получается результат UB:

enum foo {
    val0 = INT_MAX,
    val1            // equivalent to `val1 = INT_MAX+1`
};

Ответ 4

Здесь ответ С++: в 7.2/6 он указывает:

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

Таким образом, по сравнению с поведением C: no undefined, если компилятор не может найти тип, и компилятор не может просто использовать свой 512-разрядный расширенный целочисленный тип для вашего двухзначного перечисления.

Это означает, что в вашем примере базовый тип, вероятно, будет некоторым подписанным 64-битным типом - большинство компиляторов всегда сначала используют подписанную версию типа.