Расширение целочисленных типов
Если новый тип шире, чем старый (sizeof), то надо каким-то образом расширить значение
Смотрим на то, был ли тип знаковым
Расширение будет арифметическое: Signed ⇒ если в старшем бите была единичка, то значение будет расширено единичками, чтобы у нас число осталось отрицательным Unsigned ⇒ значение будет расширяться нулями
Засады бывают в том, что тип char по стандарту бывает знаковым и беззнаковым
При расширении char, мы не знаем, как именно он расширится
⇒ надо в явном виде написать, знаковый он или беззнаковый
Если мы складываем два выражения разных размеров и разной знаковости, то они все приводятся к максимальному размеру аргумента; если есть и знаковые и беззнаковые операнды, то все выражение становится беззнаковым
Q: Если значение не помещается в long long, но помещается в unsigned long long, то это UB?
A: Signed long long и unsigned long long имеют одинаковый sizeof (=64 бита) ⇒ любое значение unsigned ll помещается в signed ll
Проблема начинается в обратную сторону: мы взяли большое число и попытались положить его в маленький тип. Если результирующий тип unsigned, то все прекрасно (старшие биты обрезаются, а младшие, сколько поместятся, помещаются в новое значение), иначе (signed) значащие биты обрезались, и получается UB
Старший бит зависит от архитектуры. Стандарт языка Си говорит, что если старое значение большого типа представимо новым знаковым типом, то все хорошо, а если не представимо, то это UB
Возникло знаковое переполнение
При знаковом переполнении мы можем попросить у компилятора возбуждать исключительную ситуацию, то есть проверять все знаковые переполнения и аварийно прерывать программу при их наличии
Взяли максимальный int, прибавили единичку, возникло знаковое переполнение