Программирование. Принципы и практика использования C++ Исправленное издание, стр. 360
Получаем следующий результат:

Объяснение этого результата таково: число 257 на два больше, чем можно представить с помощью восьми битов (255 равно “восемь единиц”), а число 129 на два больше, чем можно представить с помощью семи битов (127 равно “семь единиц”), поэтому устанавливается знаковый бит. Кстати, эта программа демонстрирует, что тип
charscucПОПРОБУЙТЕ
Напишите эти битовые комбинации на листке бумаги. Затем попытайтесь вычислить результат для
si=128Кстати, почему мы использовали функцию
print()cout << i << ' ';Однако, если бы переменная
icharprint()template<class T> void print(T i) { cout << i << '\t'; }void print(char i) { cout << int(i) << '\t'; }void print(signed char i) { cout << int(i) << '\t'; }void print(unsigned char i) { cout << int(i) << '\t'; }
• Никогда не используйте целые числа без знака просто для того, чтобы получить еще один бит точности.
• Если вам необходим один дополнительный бит, то вскоре вам потребуется еще один.
• Индексирование контейнеров в стандартной библиотеке осуществляется целыми числами без знака.
• Некоторые люди любят арифметику чисел без знака.
25.5.4. Манипулирование битами
Итак, когда мы должны манипулировать битами? Иногда они являются естественными объектами нашей предметной области, поэтому естественными операциями в таких приложениях являются операции над битами. Примерами таких приложений являются индикаторы аппаратного обеспечения (“влаги”), низкоуровневые коммуникации (в которых мы должны извлекать значения разных типов из потока байтов), графика (в которой мы должны составлять рисунки из нескольких уровней образов) и кодирование (подробнее о нем — в следующем разделе).
Для примера рассмотрим, как извлечь (низкоуровневую) информацию из целого числа (возможно, из-за того, что мы хотим передать его как набор байтов через двоичный механизм ввода-вывода).
void f(short val) // пусть число состоит из 16 битов, т.е. 2 байта{ unsigned char left = val>>8; // крайний левый // (самый старший) байт unsigned char right = val&0xff; // крайний правый // (самый младший) байт // ... bool negative = val&0x8000; // знаковый бит // ...}Такие операции не редкость. Они известны как “сдвиг и наложение маски” (“shift and mask”). Мы выполняем сдвиг (“shift”), используя операторы
<<>>&0xffПри необходимости именовать биты часто используются перечисления. Рассмотрим пример.
enum Printer_flags { acknowledge=1, paper_empty=1<<1, busy=1<<2, out_of_black=1<<3, out_of_color=1<<4, // ...};Этот код определяет перечисление, в котором каждый элемент равен именно тому значению, которому соответствует его имя.

Такие значения полезны, потому что они комбинируются совершенно независимо друг от друга.
unsigned char x = out_of_color | out_of_black; // x = 24 (16+8)x |= paper_empty; // x = 26 (24+2)Отметим, что оператор
|=&if (x& out_of_color) { // установлен ли out_of_color? (Да, если