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

С++ Целочисленное переполнение и продвижение

Возможный дубликат:
Как действуют правила продвижения, когда разница между подписью с обеих сторон бинарного оператора отличается?

Я пытаюсь обернуть голову вокруг цельной рекламы и переполнения на С++. Я немного смущен несколькими пунктами:

a) Если у меня есть следующий сегмент кода:

int i = -15; 
unsigned j = 10; 
std::cout << i + j;

Я выхожу -5 % UINT_MAX. Это потому, что выражение i + j автоматически продвигается без знака? Я пытался прочитать стандарт (4.13):

— The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type.

Я не уверен, что я читаю это неправильно, но если это так, почему i + j заканчивается как unsigned?

b) Добавляя к предыдущему сегменту, теперь у меня есть:

int k = j + i;

Это получает оценку -5. Не следует ли сначала оценивать выражение j + i, предоставляя 4294967291 в моей системе и устанавливая, что оно равно j? Это должно быть за гранью, так это поведение undefined? Я не уверен, почему я получаю -5.

c) Если я немного изменил сегмент из a), используя short, у меня есть:

short i = -15;
unsigned short j = 10;
std::cout << i + j;

Я понял, что когда я это сделал, я получаю тот же результат, что и a), только с -5 % USHRT_MAX. Однако, когда я выполняю это, я получаю -5. Почему использование short дает другое значение, чем int?

d) Я всегда знал, что поведение переполнения подписанного интеграла undefined. Например: int r = ++INT_MAX будет undefined.

Однако, если было неподписанное переполнение, количество будет определено. Например: unsigned a = ++UINT_MAX, тогда a будет 0. Это правильно?

Однако стандарт ничего не сказал об этом. Это правда? Если да, то почему?

4b9b3361

Ответ 1

a) Из § 5/9:

Многие двоичные операторы, которые ожидают операнды арифметики или типа перечисления, вызывают конверсии и дают аналогичные результаты. Цель состоит в том, чтобы дать общий тип, который также является типом результата. Этот шаблон называется обычными арифметическими преобразованиями, которые определяются следующим образом:

  • Если любой из операндов имеет тип long double, другой должен быть преобразован в long double.
  • В противном случае, если любой операнд double, другой должен быть преобразован в double.
  • В противном случае, если любой операнд float, другой должен быть преобразован в float.
  • В противном случае интегральные акции (4.5) должны выполняться на обоих операндах.
  • Затем, если любой операнд unsigned long, другой должен быть преобразован в unsigned long.
  • В противном случае, если один операнд является long int, а другой unsigned int, то если a long int может представлять все значения unsigned int, unsigned int преобразуется в long int; в противном случае оба операнда должны быть преобразованы в unsigned long int.
  • В противном случае, если любой операнд long, другой должен быть преобразован в long.
  • В противном случае, если любой операнд unsigned, другой должен быть преобразован в unsigned.

[Примечание: в противном случае единственным оставшимся случаем является то, что оба операнда int]

Следовательно, поскольку j unsigned, i продвигается до unsigned, и добавление выполняется с использованием арифметики без знака.

b) Это UB. Результат добавления unsigned int (согласно (a)), и, таким образом, вы переполняете int в присваивании.

c) Из § 4.5/1:

Значение типа char, signed char, unsigned char, short int или unsigned short int может быть преобразовано в rvalue типа int, если int может представлять все значения источника тип; в противном случае исходное значение rvalue может быть преобразовано в rvalue типа unsigned int.

Поэтому, поскольку 4-байтовый int может представлять любое значение в 2-байтовом short или unsigned short, оба повышаются до int (в соответствии с правилом целых рекламных кампаний 5,9), а затем добавляются как int с.

d) Из п. 3.9.1/4:

Незнакомые целые числа, объявленные unsigned, должны подчиняться законам арифметики по модулю 2 n где n - количество бит в представлении значений этого конкретного размера целого числа.

Следовательно, UINT_MAX+1 является законным (а не UB) и равен 0.