Ответ
Для создания битовой маски с единицей в заданной позиции используется оператор левого сдвига (<<).
Базовый пример:
#include <iostream>
#include <cstdint> // Для точных типов
int main() {
int bit_position = 3; // Нумерация с 0 (младший бит)
int mask = 1 << bit_position; // 1 << 3 = 0b1000 (десятичное 8)
std::cout << "Mask (dec): " << mask << std::endl; // 8
std::cout << "Mask (hex): 0x" << std::hex << mask << std::endl; // 0x8
// Использование для проверки бита в числе
int value = 13; // 0b1101
bool is_bit_set = (value & mask) != 0;
std::cout << "Bit at position " << bit_position << " is " << (is_bit_set ? "set" : "not set") << std::endl;
return 0;
}
Критически важные нюансы:
- Переполнение и неопределённое поведение: Сдвиг на позицию, большую или равную ширине типа, приводит к UB.
int x = 1 << 32; // UB для 32-битного int! - Используйте беззнаковые типы: Для битовых операций всегда предпочтительнее беззнаковые типы, так как сдвиги знаковых чисел могут иметь платформо-зависимое поведение.
unsigned int safe_mask = 1U << bit_position; - Для 64-битных масок: Используйте суффикс
ULL.uint64_t wide_mask = 1ULL << 45; // Безопасно для позиций > 31 - Портируемый способ с проверкой:
#include <climits>
template
Ответ 18+ 🔞
А, ну это ж классика, ёпта! Сейчас объясню, как эти битовые маски делать, чтобы потом не охуеть от багов.
Смотри, в основе всего лежит оператор левого сдвига — <<. Представь, что у тебя есть единичка, и ты её толкаешь влево на определённое количество позиций. Всё просто, как три копейки.
Вот самый базовый пример, с которого всё начинается:
#include <iostream>
#include <cstdint> // Для точных типов
int main() {
int bit_position = 3; // Нумерация с 0 (младший бит)
int mask = 1 << bit_position; // 1 << 3 = 0b1000 (десятичное 8)
std::cout << "Mask (dec): " << mask << std::endl; // 8
std::cout << "Mask (hex): 0x" << std::hex << mask << std::endl; // 0x8
// Использование для проверки бита в числе
int value = 13; // 0b1101
bool is_bit_set = (value & mask) != 0;
std::cout << "Bit at position " << bit_position << " is " << (is_bit_set ? "set" : "not set") << std::endl;
return 0;
}
А теперь, чувак, самое важное — нюансы, на которых все обламываются. Вот где собака зарыта, ядрёна вошь!
-
Переполнение и неопределённое поведение — пиздец полный. Это главная ловушка. Если сдвинешь единицу на позицию, которая больше или равна разрядности твоего типа, получишь UB. Программа может сделать что угодно: скомпилироваться и молча сломаться, выдать мусор или вообще улететь в космос.
int x = 1 << 32; // UB для 32-битного int! Вот тут и накрывается медным тазом твой код. -
Используй беззнаковые типы, не будь распиздяем. Для битовых операций — только
unsigned. Со знаковыми типами (int,long) сдвиги — это тёмный лес, поведение зависит от компилятора и стандарта. Не надо так.unsigned int safe_mask = 1U << bit_position; // Вот так — правильно. -
Когда дело доходит до 64 бит — не забудь суффикс
ULL. Иначе опять попадёшь на переполнение. Хочешь сдвинуть на 45-ю позицию? Пиши1ULL.uint64_t wide_mask = 1ULL << 45; // Безопасно для позиций > 31. Всё чётко. -
Портируемый и надёжный способ, чтобы спать спокойно. Оберни эту логику в шаблон с проверкой. Тогда волнение ебать будет нулевое.
#include <climits>
template
Вот и вся магия. Главное — помни про переполнение и используй беззнаковые типы, а то будет тебе хиросима, а не битовая маска.