Побитовые операторы управляют отдельными битами целых числовых типов:
& — побитовое И| — побитовое ИЛИ^ — побитовое исключающее ИЛИ~ — побитовое НЕ<< — сдвиг влево>> — сдвиг вправоОсобенности:
int, unsigned int, и т.п.).signed) при сдвигах вправо (>>) могут порождать арифметическое или логическое смещение — зависит от компилятора.unsigned типы, чтобы избежать расширения знака.Пример:
unsigned int flags = 0; flags |= 0x1; // Установить 0-й бит flags &= ~0x2; // Сбросить 1-й бит if ((flags & 0x4) != 0) { /* ... */ } // Проверить 2-й бит
Чем отличается операция сдвига вправо (
>>) для типовsigned intиunsigned int?
Часто ошибочный ответ: Считают, что сдвиг вправо всегда вставляет нули слева, независимо от знаковости.
Правильный ответ:
Для типа unsigned int сдвиг вправо (>>) всегда вставляет нули. Для signed int вставляется либо знак (единицы, если число отрицательное), либо нули — зависит от реализации компилятора (архитектура и правила стандарта C).
Пример:
signed int a = -8; unsigned int b = (unsigned int)a; printf("%d ", a >> 1); printf("%u ", b >> 1);
В первом случае результат зависит от компилятора; во втором всегда будет логический сдвиг с нулями.
История
В коде обработки протокола сигнальные флаги сохранялись в типе char. Программист применил сдвиг на 8 бит (flag << 8), что из-за переполнения и правил повышения типов привело к потере всех данных — result был всегда равен нулю.
История
Чтение данных из сетевого протокола (big-endian). Использование побитовых операций для объединения байтов не сопровождалось приведением к unsigned, что иногда приводило к неожиданным отрицательным значениям при чтении поля структуры.
История
Использование ~ (побитового НЕ) для сброса битов в значении типа int (например, ~0x80) принималось за 0x7F, но в реальности получалось отрицательное число -129, что приводило к ошибкам при последующих вычислениях и логических проверках.