Определено ли смещение отрицательных целых чисел влево и вправо? - PullRequest
16 голосов
/ 07 декабря 2011

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

int i = -1;
i << 1;

Это четко определено?

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

, если E1 имееттип со знаком и неотрицательное значение, и E1 × 2 E2 представимо в типе результата, то есть полученное значение;в противном случае поведение не определено.

Это только поясняет, что если результат не может быть представлен в типе со знаком, то поведение не определено.

Ответы [ 3 ]

28 голосов
/ 07 декабря 2011

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

16 голосов
/ 18 апреля 2015

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

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

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

  • , если y является типом со знаком, x << yи x >> y оцениваются, как если бы y был приведен к неподписанному.
  • , если 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 отрицательно, это может потенциально быть long времени, хотя я знаю только одну машину, которая бына практике выполнить более 256 дополнительных шагов].Компиляторы не обязательно были последовательны в своем выборе, но всегда возвращали одно из указанных значений без других побочных эффектов.

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

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

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

3 голосов
/ 07 декабря 2011

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

Сюда входит

, если E1 имеет тип со знаком и неотрицательное значение

...