A char
- 8 бит. Это означает, что он может представлять 2 ^ 8 = 256 уникальных значений. uchar
представляет от 0 до 255, а подписанный char
представляет от -128 до 127 (может представлять абсолютно все, но это типичная реализация платформы). Таким образом, присвоение 130 для char
выходит за пределы диапазона на 2, и значение переполняется и переносит значение в -126, когда оно интерпретируется как подписанное char
. Компилятор видит 130 как целое число и делает неявное преобразование из int
в char
. На большинстве платформ int 32-битный, а знаковый бит - это MSB, значение 130 легко помещается в первые 8 бит, но затем компилятор хочет выделить 24 бита, чтобы сжать его в символ. Когда это происходит, и вы сказали компилятору, что хотите подписанный символ, MSB первых 8 битов фактически представляет -128. Ой! Теперь у вас есть это в памяти 1000 0010
, которое при интерпретации как подписанный символ равно -128 + 2. Мой носитель на моей платформе кричит об этом. ,
Я подчеркиваю это важное замечание об интерпретации, поскольку в памяти оба значения идентичны. Вы можете подтвердить это, приведя значение в операторы printf
, то есть printf("3: %+d\n", (unsigned char)c1);
, и вы снова увидите 130.
Причина, по которой вы видите большое значение в своем первом операторе printf
, заключается в том, что вы преобразуете char
со знаком в неподписанное int
, где char
уже переполнено. Машина сначала интерпретирует char
как -126, а затем переводит в беззнаковое int
, которое не может представлять это отрицательное значение, поэтому вы получаете максимальное значение со знаком int
и вычитаете 126.
2 ^ 32-126 = 4294967170. , лото
В операторе 2 printf
все, что нужно сделать машине, это добавить 24 ноля для достижения 32-разрядного значения, а затем интерпретировать значение как int
. В первом утверждении вы сказали, что у вас есть знаковое значение, поэтому сначала оно превращается в 32-разрядное значение -126, а затем интерпретирует это целое -ve как целое число без знака. Снова, это переворачивает, как это интерпретирует самый важный бит Есть 2 шага:
- Подпись char повышается до подписи int, потому что вы хотите работать с int. Символ (вероятно, скопирован и) имеет 24 добавленных бита. Поскольку мы смотрим на значение со знаком, некоторые машинные инструкции будут выполнять дополнение к двум, поэтому память здесь выглядит совсем иначе.
- Новая подписанная int-память интерпретируется как unsigned, поэтому машина смотрит на MSB и интерпретирует его как 2 ^ 32 вместо -2 ^ 31, как это произошло в повышении.
Интересная мелочь: вы можете подавить предупреждение clang-tidy linter, если вы сделаете char c1 = 130u;
, но вы все равно получите тот же мусор на основе вышеуказанной логики (то есть неявное преобразование отбрасывает первые 24 бита и знаковый бит все равно был равен нулю). Я отправил отчет об отсутствующей функциональности LLVM clang-tidy, основанный на изучении этого вопроса (проблема 42137 , если вы действительно хотите следовать ему) ?.