Я изучаю язык C ++ и у меня есть некоторые сомнения по поводу преобразования типов, не могли бы вы объяснить, что происходит в выражении, подобном этому:
unsigned int u = 10;
int a = -42;
std::cout << u - a << std::endl;
Здесь я знаю, что результатом будет 52, если я приму правила, когда у нас есть два математических оператора. Но мне интересно, что произойдет, когда компилятор для преобразования в беззнаковое значение создаст временный тип без знака, что произойдет после? Выражение теперь должно быть 10 -4294967254.
Проще говоря, если вы смешиваете типы одного ранга (в последовательности int
, long int
, long long int
), беззнаковый тип «выигрывает», и вычисления выполняются в пределах этого беззнакового типа. Результат того же типа без знака.
Если вы смешиваете типы с разным рангом, тип с более высоким рейтингом «выигрывает», если он может представлять все значения с более низким рейтингом. Расчеты выполняются в пределах этого типа. Результат такого типа.
Наконец, если тип с более высоким рейтингом не может представлять все значения с типом с более низким рейтингом, то используется версия без знака с типом с более высоким рейтингом. Результат такого типа.
В вашем случае вы смешали типы одного ранга (int
а также unsigned int
), что означает, что все выражение оценивается в unsigned int
тип. Выражение, как вы правильно сказали, теперь 10 - 4294967254
(для 32 бит int
). Типы без знака подчиняются правилам арифметики по модулю 2^32
(4294967296
) по модулю. Если вы тщательно рассчитаете результат (который можно выразить арифметически как 10 - 4294967254 + 4294967296
) получится как положено 52
,
1) Из-за стандартных правил продвижения signed
тип a
повышен до unsigned
введите до вычитания. Это продвижение происходит в соответствии с этим правилом (стандарт C ++ 4.7 / 2):
Если тип назначения не подписан, полученное значение является наименьшим
целое число без знака, совпадающее с целым числом источника (по модулю 2n, где n
количество битов, используемых для представления типа без знака).
Алгебраически a
становится очень большим положительным числом и, конечно, больше, чем u
,
2) u - a
является анонимным временным и будет неподписанный тип. (Вы можете проверить это, написав auto t = u - a
и проверка типа t
в вашем отладчике.) Математически это будет отрицательное число, но при неявном преобразовании в беззнаковый тип вызывается правило циклического переноса, подобное приведенному выше.
Короче говоря, две операции преобразования имеют равные и противоположные эффекты, и результат будет 52. На практике компилятор может оптимизировать все эти преобразования.
вот код дизассемблирования говорит:
это сначала устанавливает -42
в дополнение и сделать субоперацию. так что результат 10 + 42
0x0000000000400835 <+8>: movl $0xa,-0xc(%rbp)
0x000000000040083c <+15>: movl $0xffffffd6,-0x8(%rbp)
0x0000000000400843 <+22>: mov -0x8(%rbp),%eax
0x0000000000400846 <+25>: mov -0xc(%rbp),%edx
0x0000000000400849 <+28>: sub %eax,%edx
0x000000000040084b <+30>: mov %edx,%eax