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

Является ли левое и правое смещение отрицательных целых чисел определенным поведением?

Я знаю, что правильный сдвиг отрицательного подписи зависит от реализации, но что делать, если я выполняю сдвиг влево? Например:

int i = -1;
i << 1;

Является ли это корректным?

Я думаю, что стандарт не говорит о отрицательном значении с подписанным типом

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

Он только уточняет, что если результат не представлен в подписанном типе, то поведение undefined.

4b9b3361

Ответ 1

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

Ответ 2

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

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

На практике, примерно до 5 лет назад, 99%% компиляторов, написанных для машины, которая использовала математику с двумя дополнениями (что означает 99%% машин, созданных с 1990 года), последовательно приводило бы к следующим поведениям для x<<y и x>>y, в той степени, в которой зависимость от такого поведения кода считалась не более переносимой, чем код, который предположил, что char составлял 8 бит. Стандарт C не предусматривал такого поведения, но любой автор компилятора, желающий быть совместимым с широкой базой существующего кода, будет следовать за ним.

  • если y является подписанным типом, x << y и x >> y оцениваются так, как будто y был добавлен в unsigned.
  • Если x - тип int, x<<y эквивалентен (int)((unsigned)x << y).
  • если x - тип int и положительный, x>>y эквивалентен (unsigned)x >> y. Если x имеет тип int и отрицательный, x>>y эквивалентен `~ (~ ((без знака) x) → y).
  • Если x имеет тип long, применяются аналогичные правила, но с unsigned long, а не unsigned.
  • Если x является N-разрядным типом, а y больше N-1, то x >> y и x << y могут произвольно давать нуль или могут действовать так, как если бы правый операнд был y % N; они могут потребовать дополнительного времени, пропорционального y [обратите внимание, что на 32-битной машине, если y отрицательно, это может быть потенциально долгое время, хотя я знаю только одну машину, которая на практике запускает более 256 дополнительные шаги]. Компиляторы не всегда были последовательными по своему выбору, но всегда возвращали одно из указанных значений без каких-либо других побочных эффектов.

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

uint32_t shiftleft(uint32_t v, uint8_t n)
{
  if (n >= 32)
    v=0;
  return v<<n;
}

компилятор может определить, что, поскольку код будет участвовать в Undefined Поведении, когда n равно 32 или больше, компилятор может предположить, что if никогда не вернет true и, таким образом, может опустить код. Следовательно, если или пока кто-то не придумает стандарт для C, который восстанавливает классическое поведение и позволяет программистам определять, какие предположения заслуживают удаления мертвого кода, такие конструкции не могут быть рекомендованы для любого кода, который может быть передан в гиперсовременный компилятор.

Ответ 3

в противном случае поведение undefined.

Это включает в себя

если E1 имеет подписанный тип и неотрицательное значение