Этот небольшой тест на самом деле более тонкий, чем кажется, поскольку поведение определяется реализацией:
unsigned char x = 255;
здесь нет двусмысленности, x
- это unsigned char
со значением 255
, тип unsigned char
гарантированно имеет достаточный диапазон для хранения 255
.
printf("%x\n", x);
Это дает ff
на стандартный вывод, но было бы чище написать printf("%hhx\n", x);
, поскольку printf
ожидает unsigned int
для преобразования %x
, а x
- нет. Передача x
может фактически передать int
или unsigned int
аргумент.
unsigned char tmp = x << 7;
Чтобы оценить выражение x << 7
, x
, являющееся unsigned char
, сначала проходит целочисленное повышение, определенное в стандарте C 6.3.3.1: Если int
может представлять все значения исходный тип (ограниченный шириной для битового поля), значение преобразуется в int
; в противном случае он преобразуется в unsigned int
. Это называется целочисленными рекламными акциями.
Таким образом, если количество битов значения в unsigned char
меньше или равно int
(наиболее распространенный случай в настоящее время 8 против 31), x
сначала повышается до int
с тем же значением, которое затем сдвигается влево на 7
позиций. Результат, 0x7f80
, гарантированно соответствует типу int
, поэтому поведение четко определено, и преобразование этого значения в тип unsigned char
эффективно усекает старшие биты значения. Если тип unsigned char
имеет 8 бит, значение будет 128
(0x80
), но если тип unsigned char
имеет больше бит, значение в tmp
может быть 0x180
, 0x380
, 0x780
, 0xf80
, 0x1f80
, 0x3f80
или даже 0x7f80
.
Если тип unsigned char
больше, чем int
, что может происходить в редких системах, где sizeof(int) == 1
, x
повышается до unsigned int
, и для этого типа выполняется сдвиг влево. Значение равно 0x7f80U
, что гарантированно соответствует типу unsigned int
, и сохранение его в tmp
фактически не приводит к потере информации, поскольку тип unsigned char
имеет тот же размер, что и unsigned int
. Таким образом, tmp
в этом случае будет иметь значение 0x7f80
.
unsigned char y = tmp >> 7;
Оценка выполняется так же, как указано выше, tmp
повышается до int
или unsigned int
в зависимости от системы, которая сохраняет его значение, и это значение сдвигается вправо на 7 позиций, что полностью определено, поскольку 7
меньше ширины типа (int
или unsigned int
) и значение положительное. В зависимости от количества битов типа unsigned char
значение, хранимое в y
, может быть 1
, 3
, 7
, 15
, 31
, 63
, 127
или 255
, наиболее распространенная архитектура будет иметь y == 1
.
printf("%x\n", y);
опять же, было бы лучше написать printf("%hhx\n", y);
, и результат может быть 1
(наиболее частый случай) или 3
, 7
, f
, 1f
, 3f
, 7f
или ff
в зависимости от количества битов значения в типе unsigned char
.
unsigned char z = (x << 7) >> 7;
Целочисленное продвижение выполняется на x
, как описано выше, значение (255
) затем сдвигается влево на 7 битов как int
или unsigned int
, всегда дает 0x7f80
, а затем сдвигается вправо на 7 позиций с конечным значением 0xff
. Это поведение полностью определено.
printf("%x\n", z);
Еще раз, строка формата должна быть printf("%hhx\n", z);
, а на выходе всегда будет ff
.
Системы, в которых байты имеют более 8 бит, в наши дни становятся редкостью, но некоторые встроенные процессоры, такие как специализированные DSP, все еще делают это. При передаче unsigned char
в качестве %x
спецификатора преобразования потребовалась бы извращенная система, но лучше использовать %hhx
или более переносимо писать printf("%x\n", (unsigned)z);
(x<<7)>>7
в принципе также сохраняет промежуточный результат. Но я не знаю, где написано, какого типа должен быть этот промежуточный результат. - person The Photon   schedule 22.05.2020(x << 7) >> 7
, равенint
илиunsigned int
в зависимости от размеровunsigned char
иint
. - person chqrlie   schedule 22.05.2020