Ответ 1
Соответствующая цитата из Стандарта:
5 Выражения [expr]
10 Многие двоичные операторы, ожидающие операндов арифметических или тип перечисления вызывает преобразования и дает результаты в аналогичных путь. Цель состоит в том, чтобы дать общий тип, который также является типом результат. Этот шаблон называется обычным арифметическим преобразованием, которые определяются следующим образом:
[2 статьи о равных типах или типах знаков равенства опущены]
- В противном случае, если операнд, который имеет целочисленный тип без знака, имеет ранг больше или равно рангам типа другого операнда, операнд со знаком целочисленного типа должен быть преобразован в тип операнд с целым числом без знака.
- В противном случае, если тип операнд со знаком целочисленного типа может представлять все значения типа операнда с целым типом без знака, операнд с целым числом без знака, должны быть преобразованы в тип операнд со знаком целочисленного типа.
- В противном случае оба операнда должны быть преобразуется в целочисленный тип без знака, соответствующий типу операнд со знаком целочисленного типа.
Рассмотрим следующие 3 примерных случая для каждого из трех вышеприведенных положений о системе, где sizeof(int) < sizeof(long) == sizeof(long long)
(легко адаптируемая к другим случаям)
#include <iostream>
signed int s1 = -4;
unsigned int u1 = 2;
signed long int s2 = -4;
unsigned int u2 = 2;
signed long long int s3 = -4;
unsigned long int u3 = 2;
int main()
{
std::cout << (s1 + u1) << "\n"; // 4294967294
std::cout << (s2 + u2) << "\n"; // -2
std::cout << (s3 + u3) << "\n"; // 18446744073709551614
}
Живой пример с выходом.
Первое предложение: типы равного ранга, поэтому операнд signed int
преобразуется в unsigned int
. Это влечет за собой преобразование значения, которое (с использованием двух дополнений) дает печатное значение.
Второе предложение: подписанный тип имеет более высокий ранг и (на этой платформе!) может представлять все значения неподписанного типа, поэтому неподписанный операнд преобразуется в тип подписи, и вы получаете -2
Третье предложение: тип подписи снова имеет более высокий ранг, но (на этой платформе!) не может представлять все значения неподписанного типа, поэтому оба операнда преобразуются в unsigned long long
, а после преобразования значений в подписанном операнде, вы получаете напечатанное значение.
Обратите внимание, что когда неподписанный операнд будет достаточно большим (например, 6 в этих примерах), тогда конечный результат даст 2 для всех 3 примеров из-за переполнения целых чисел без знака.
(Добавлено) Обратите внимание, что вы получаете еще более неожиданные результаты при сравнении этих типов. Рассмотрим вышеприведенный пример 1 с помощью <
:
#include <iostream>
signed int s1 = -4;
unsigned int u1 = 2;
int main()
{
std::cout << (s1 < u1 ? "s1 < u1" : "s1 !< u1") << "\n"; // "s1 !< u1"
std::cout << (-4 < 2u ? "-4 < 2u" : "-4 !< 2u") << "\n"; // "-4 !< 2u"
}
Так как 2u
явно выражается unsigned
явно суффиксом u
, применяются те же правила. И результат, вероятно, не тот, который вы ожидаете при сравнении -4 < 2 при записи на С++ -4 < 2u
...