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

Является ли 1 << 31 хорошо определенным в C, когда sizeof (int) == 4

В соответствии с ответом на this questions:

Результат E1 < E2 - это E1 левое смещение E2; освобожденные биты заполняются нулями. Если E1 имеет неподписанный тип, значение результата E1 × 2 E2, приведенное по модулю больше, чем максимальное значение, представляемое в результат. Если E1 имеет подписанный тип и неотрицательное значение, а E1 × 2 E2 представляется в типе результата, то это результирующее значение; в противном случае поведение не определено.

Похоже, что 1 << 31 - undefined.

Однако GCC не выдает предупреждение, если я использую 1 << 31. Он выпускает один для 1 << 32. ссылка

Итак, что это? Я не понимаю стандарт? У GCC есть своя интерпретация?

4b9b3361

Ответ 1

Нет: 1 << 31 имеет поведение undefined, если тип int имеет только 31 бит значения.

1U << 31 ОК и оценивается как 0x80000000, если тип unsigned int имеет 32 бита значения.

В системе, где байты имеют 8 бит, sizeof(int) == 4 означает int имеет не более 31 битов значений, поэтому сдвиг 1 на 31 место равен undefined. И наоборот, в системе, где CHAR_BIT > 8, может быть нормально писать 1 << 31.

gcc может выдать предупреждение, если вы повышаете уровень предупреждения. попробуйте gcc -Wall -Wextra -W -Werror. clang выводит предупреждение с теми же параметрами.

Чтобы обратиться к комментариям Майкл Рой, 1 << 31 надежно не оценивает INT_MIN. Он может дать это значение в вашей системе, но стандарт не гарантирует этого, на самом деле стандарт описывает это как поведение undefined, поэтому вы можете не только не полагаться на него, вы должны избегать его, чтобы избежать ложных ошибок. Оптимизаторы обычно используют потенциальное поведение undefined для удаления кода и нарушают предположения программистов.

Например, следующий код может скомпилировать простой return 1;:

int check_shift(int i) {
   if ((1 << i) > 0)
       return 1;
   else
       return 0;
}

Ни один из компиляторов, поддерживаемый Godbolt explorer, делает это, но это не нарушит соответствия.

Ответ 2

Причина, по которой GCC не предупреждает об этом, заключается в том, что 1 << 31 был действителен (но определен в реализации) в C90 и действителен (но определен в реализации) даже в современном С++. C90 определяет << как сдвиг бит и за ним следует, что для неподписанных типов его результатом является результат умножения, но не делал такого типа для подписанных типов, что неявно делало его действительным и оставляло его охватываемым общей формулировкой, которая побитовая операторы имеют определенные для реализации аспекты для подписанных типов. С++ в настоящее время определяет << как умножающийся на соответствующий неподписанный тип, при этом результат преобразуется обратно в тип подписи, который также определяется реализацией.

C99 и C11 сделали это недопустимым (говорят, что поведение undefined), но компиляторам разрешено принимать его как расширение. Для совместимости с существующим кодом и совместного использования кода между интерфейсами C и С++ GCC продолжает это делать, за одним исключением: вы можете использовать -fsanitize=undefined для обнаружения обнаруженного поведения undefined для прерывания вашей программы во время выполнения и этот обрабатывает 1 << 31, но только при компиляции как C99 или C11.

Ответ 3

Он вызывает поведение undefined, как объясняют другие ответы/комментарии. Однако, почему GCC не испускает диагностику.

На самом деле существует две вещи, которые могут привести к поведению undefined для сдвига влево (как из [6.5.7]):

  • Если значение правильного операнда отрицательное или больше или равно ширине продвинутого левого операнда, поведение undefined.

  • Если E1 имеет подписанный тип и неотрицательное значение, а E1 × 2 E2 является представимым в типе результата, то это результирующее значение; в противном случае поведение undefined.

Очевидно, что GCC обнаруживает первый (потому что он тривиальен для этого), но не последний.