Если char c = 0x80, почему printf("%d\n", c << 1) выдает -256?
#include<stdio.h>
int main(void)
{
char c = 0x80;
printf("%d\n", c << 1);
return 0;
}
Выход -256
в этом случае. Если я напишу c << 0
тогда вывод -128
,
Я не понимаю логику этого кода.
5 ответов
Уже ваша отправная точка проблематична:
char c = 0x80;
Если (как бы в вашем случае) char
является типом со знаком, вы назначаете целочисленную константу 128
к типу, который гарантированно содержит только значения 127
, Затем ваш компилятор может выбрать какое-то определенное для реализации значение (-128
в вашем случае, я думаю) или выдать ошибку диапазона.
Затем вы делаете сдвиг влево на это отрицательное значение. Это дает неопределенное поведение. Всего у вас есть несколько вариантов реализации, определенных плюс поведение, определяющее результат:
- подпись
char
- выбор как конвертировать
128
вsigned char
- ширина
char
- знаковое представление
int
(есть три варианта) - выбор того, как реализовать (или нет) левый сдвиг на минус
int
Это может быть хорошим упражнением для вас, чтобы посмотреть все эти случаи и посмотреть, каковы могут быть различные результаты.
В заключение несколько рекомендаций:
- выберите подходящую константу для инициализации переменной
- не делайте арифметику с простым
char
- не делайте сдвиг влево на подписанных типах
char
может быть подписан на вашей платформе, в этом случае 0x80
представляет -128 (при условии дополнения до двух).
Когда char
используется в качестве операнда с <<
оператор, он повышен до int
(до сих пор -128). Поэтому, когда вы применяете левый сдвиг, вы получаете -256. Технически, сдвиг отрицательных значений не определен реализацией, но то, что вы видите, является типичным поведением.
c
назначен 0x80
, Предполагая 8-битные байты, его значение в двоичном представлении, является 10000000
, Видимо, на вашей платформе, char
это подписанный тип. Так, 0x80
(т.е. 10000000
) соответствует -128.
когда <<
применяется к char
значение, это повышается до int
и знак сохраняется. Таким образом, при сдвиге влево с 32-разрядными целыми числами он становится 11111111111111111111111100000000
(дополнение двух), которое составляет -256.
Просто примечание. С точки зрения снизу вверх, побитовое смещение (и маскирование) основано на длине слова архитектуры (выраженной в битах). Длина слова варьируется от архитектуры к архитектуре.
Смотрите эту вики-страницу для длины слова по архитектуре
Если известно длина слова целевой архитектуры, можно использовать сдвиг битов для умножения и деления (в некоторых случаях) быстрее, чем с использованием операндов.
Смотрите эту вики-страницу для интересных диаграмм сдвига битов
Поскольку код со сдвигом битов зависит от архитектуры, нельзя предполагать, что конкретная часть кода со сдвигом битов будет работать одинаково от архитектуры к архитектуре. Однако, как только человек знаком с идеей разной длины слов для разных архитектур, сдвиг битов становится менее загадочным и более предсказуемым.
К счастью, сегодня у нас есть 8, 16, 32 и 64-битные длины слов и исключительно 8-битные длины символов. Во времена древних вычислений архитектура могла иметь длину слова 12, 15 или 23 бита (и т. Д., Ad nauseum).
Интересно, почему ваш компилятор не жалуется на предупреждение, что 0x80 не помещается в char, который на вашей платформе может представлять только значения от -0x80 до 0x7F.
Попробуйте этот кусок кода:
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
int main() {
printf("char can represent values from %d to %d.\n", CHAR_MIN, CHAR_MAX);
return EXIT_SUCCESS;
}
Ваша ситуация называется OVERFLOW.