Знаковые значения обычно хранятся в виде чего-то, называемого дополнением до двух :
Числа дополнения до двух являются способом кодирования отрицательных чисел в обычный двоичный код, так что сложение все еще работает.Добавление -1 + 1 должно равняться 0, но обычное сложение дает результат 2 или -2, если только операция не принимает специального знака о знаковом бите и вместо этого не выполняет вычитание.Два дополнения приводят к правильной сумме без этого дополнительного шага.
Это означает, что фактическое представление чисел -5 и 4294967291 в памяти (для 32-битного слова) идентично, например: 0xFFFFFFFB
или 0b11111111111111111111111111111011
.Поэтому, когда вы делаете:
unsigned int y = x;
Содержимое x копируется дословно, то есть поразрядно в y
.Это означает, что если вы проверяете необработанные значения в памяти x
и y
, они будут идентичны.Однако если вы сделаете:
unsigned long long y1 = x;
, значение x
будет расширено до знака перед преобразованием в длинный без знака.В общем случае, когда long long составляет 64 бита, это означает, что y1
равно 0xFFFFFFFFFFFFFFFB
.
Важно отметить, что происходит при приведении к типу большего размера.Значение со знаком, приведенное к большему значению со знаком, будет расширено со знаком.Этого не произойдет, если исходное значение не подписано, например:
unsigned int z = y + 5;
long long z1 = (long long)x + 5; // sign extended since x is signed
long long z2 = (long long)y + 5; // not sign extended since y is unsigned
z
и z1
будут равны 0, а z2
- нет.Это можно исправить, приведя значение к знаку до , развернув его:
long long z3 = (long long)(signed int)y + 5;
или аналогично, если вы не хотите, чтобы произошло расширение знака:
long long z4 = (long long)(unsigned int)x;