C++로 개발하다 보면, 비트 연산을 꽤 자주 쓰게 됩니다. 특히 상태나 옵션을 여러 개 동시에 표현해야 할 때, Bitmask나 Bit flags(비트 플래그)가 상당히 편리하죠. 이번 포스팅에서는 Bitmask, Bit flags 개념부터 기본 연산, 사용 예시까지 한 번에 정리해보려고 합니다.
Bitmask
특정 비트를 골라내거나, 반대로 특정 비트들만 집중적으로 다루기 위해 사용하는 “Mask” 같은 값입니다. 예를 들어 0xFF(16진수 255)는 하위 8비트만 “가려내서” 사용하고 싶을 때 쓰는 등, 원하는 비트 영역만 추려낼 수 있죠.
Bit Flags(비트 플래그)
정수 타입의 각 비트를 ON/OFF 스위치처럼 써서, 여러 상태나 옵션을 한 변수에 담아 표현하는 방법입니다. 예를 들어,
status)에 여러 상태를 겹쳐서 표현할 수 있어요.다음 코드는 RPG 게임 캐릭터의 상태를 비트 플래그로 표현하는 간단한 예시입니다.
#include <iostream>
// 비트 플래그 정의
constexpr int STOP = 1 << 0; // 0001 (2^0 = 1)
constexpr int WALK = 1 << 1; // 0010 (2^1 = 2)
constexpr int RUN = 1 << 2; // 0100 (2^2 = 4)
int main() {
int status = 0; // 처음엔 어떤 상태도 없음
// 정지와 걷기 상태를 추가
status |= STOP;
status |= WALK;
// 현재 상태 확인
if (status & STOP) {
std::cout << "정지 상태입니다.\n";
}
if (status & WALK) {
std::cout << "걷는 중입니다.\n";
}
// 마비 상태를 해제하기
status &= ~RUN;
// 다시 확인
if (status & RUN) {
std::cout << "달리는 중입니다.\n";
} else {
std::cout << "달리기를 멈춥니다.\n";
}
return 0;
}
status |= FLAG; : status 변수의 0번째 비트를 켜서(1로 만들어서) FLAG를 작동합니다.status &= ~FLAG; : status 변수의 1번째 비트를 꺼서(0으로 만들어서) FLAG를 해제합니다.이처럼 플래그(FLAG)는 1 << N 형태로 정의해서 쓰는 게 가장 흔한 방법이에요.
비트 플래그를 활용하기 위해서는 기본적인 비트 연산을 익혀두면 편해요.
특정 비트를 켜기(Set)
status |= FLAG;
예: status |= (1 << 3); // 3번째 비트 켜기
특정 비트를 끄기(Clear)
status &= ~FLAG;
예: status &= ~(1 << 3); // 3번째 비트 끄기
특정 비트를 토글(Toggle)
status ^= FLAG;
예: status ^= (1 << 3); // 3번째 비트가 켜져있으면 끄고, 꺼져있으면 켠다
특정 비트가 켜져있는지 확인(Check)
if (status & FLAG) {
// 켜져있다면...
}
예:
if (status & (1 << 3)) {
std::cout << "3번째 비트가 켜져 있습니다.\n";
}
enum, constexpr로 플래그 정의하기
위 예시처럼 constexpr int로 정의하는 방법이 가장 간단해요. C++11 이상의 enum class를 써서 형 안전성(Strongly typed)을 좀 더 챙기는 경우도 있습니다. 예를 들어:
enum class StatusFlags : int {
STOP = 1 << 0,
WALK = 1 << 1,
RUN = 1 << 2,
// ...
};
이렇게 하면, static_cast<int>(StatusFlags::RUN) 같은 식으로 캐스팅을 해줘야 하긴 하지만, 중복된 상수 이름 충돌을 줄일 수 있다는 장점이 있어요.
연산자 오버로딩
enum class로 만들면 그냥 |= 같은 비트 연산이 바로 안 되기 때문에, 연산자 오버로딩을 통해 “코드 가독성”을 높이는 패턴도 있습니다.
inline StatusFlags operator|(StatusFlags a, StatusFlags b) {
return static_cast<StatusFlags>(static_cast<int>(a) | static_cast<int>(b));
}
이렇게 해주면 StatusFlags f = StatusFlags::STOP | StatusFlags::WALK; 같은 표현이 가능해지죠.
플래그가 너무 많다면?
int는 32비트, long long은 64비트까지 사용할 수 있지만, 플래그가 70개, 100개를 넘어가면 조금 부담됩니다. 그럴 때는 비트마스크보다도 다른 자료구조(예: std::bitset)를 쓰거나, 구조체나 클래스로 나눠서 관리하는 편이 낫습니다.
enum 등을 통해 의미를 명확히 해주세요.int인지 long long인지, 또는 std::bitset인지 미리 정해두고 일관성 있게 쓰는 게 좋아요.C++에서 Bitmask와 Bit Flags는 여러 상태를 한 번에 관리하기에 정말 편리한 도구입니다. 공간 절약부터 빠른 연산까지 장점이 많아요. 다만 플래그가 너무 많으면 오히려 헷갈릴 수 있으니, 적절한 선에서 사용하고, 가독성을 위해 enum class나 오버로딩 같은 기법도 잘 활용하면 좋겠죠.
이 포스팅이 도움이 되셨길 바랍니다. 궁금한 점이나 다른 의견이 있다면 언제든 댓글 남겨주세요!