CPP 여러가지 연산자, 비트플래그

산술 연산

대입과 사칙연산

int a = 10;             // 대입: a에 10을 저장하고 10 반환
int b = a;              // b에 a값 복사

// 사칙연산
int sum = a + b;        // 덧셈
int diff = a - b;       // 뺄셈  
int product = a * b;    // 곱셈
int quotient = a / b;   // 나눗셈(몫)
int remainder = a % b;  // 나머지

// 복합 대입
a += 3;                 // a = a + 3;
a *= 2;                 // a = a * 2;

증감 연산자

int a = 5;

int b = a++;            // b = 5, a = 6 (후위: 대입 후 증가)
int c = ++a;            // c = 7, a = 7 (전위: 증가 후 대입)

int d = a--;            // d = 7, a = 6 (후위: 대입 후 감소)
int e = --a;            // e = 5, a = 5 (전위: 감소 후 대입)

연산자 우선순위: *, /, %+, -=


비교 연산

int hp = 30, maxHP = 100;

bool isDead = (hp == 0);              // 같은가?
bool isAlive = (hp != 0);             // 다른가?
bool isLowHP = (hp < maxHP * 0.3);    // 30% 미만인가?
bool isFullHP = (hp >= maxHP);        // 만피 이상인가?

모든 비교 연산은 1(true) 또는 0(false) 반환.


논리 연산

기본 연산자

bool isInvincible = true;
bool isDead = false;

// NOT (!)
bool canTakeDamage = !isInvincible;   // false

// AND (&&) - 둘 다 true여야 true
bool shouldDie = (hp <= 0 && !isInvincible);

// OR (||) - 하나라도 true면 true  
bool isAlive = (hp > 0 || isInvincible);

단축 평가 (Short-Circuit Evaluation)

C++ 문법으론 같은 결과일지라도 어셈블리에서 근소한 차이를 보이기에 성능 최적화를 위해 빠른 조건을 앞쪽에 작성하는게 좋음.

AND (&&): 왼쪽이 false면 오른쪽 실행 안 함

bool result = (hp <= 0 && checkInvincibility());
// hp > 0이면 checkInvincibility() 호출 안 됨

OR (||): 왼쪽이 true면 오른쪽 실행 안 함

bool result = (isGod || hasCheatCode());
// isGod가 true면 hasCheatCode() 호출 안 됨

성능 최적화:

// 좋은 예: 빠른 조건을 앞에
if (cheapCheck() && expensiveCheck()) { }

// 나쁜 예: 느린 조건을 앞에
if (expensiveCheck() && cheapCheck()) { }

비트 연산

기본 연산자

unsigned char a = 0b1100;   // 12
unsigned char b = 0b1010;   // 10

cout << (a & b);            // 0b1000 = 8 (AND)
cout << (a | b);            // 0b1110 = 14 (OR)  
cout << (a ^ b);            // 0b0110 = 6 (XOR)
cout << (~a);               // 0b0011 = 3 (NOT, 4비트 기준)

비트 시프트

unsigned int x = 5;         // 0b101

cout << (x << 1);           // 0b1010 = 10 (*2 효과)
cout << (x << 2);           // 0b10100 = 20 (*4 효과)
cout << (x >> 1);           // 0b10 = 2 (/2 효과)

컴파일러 최적화: x * 2x << 1로 자동 변환됨


비트플래그 (BitFlag)

상태이상에서의 활용

// 0b0000 [무적][기절][변이][속박]
//  bit:   3    2    1    0

enum StatusEffect {
    BIND = 1 << 0,          // 0b0001
    POLYMORPH = 1 << 1,     // 0b0010  
    STUN = 1 << 2,          // 0b0100
    INVINCIBLE = 1 << 3     // 0b1000
};

unsigned int flag = 0;

상태 추가/제거

// 무적 상태 추가
flag |= INVINCIBLE;         // 0b1000

// 기절 상태 추가  
flag |= STUN;               // 0b1100

// 무적 상태 제거
flag &= ~INVINCIBLE;        // 0b0100

// 모든 상태 제거
flag = 0;

상태 확인

// 무적 상태인가?
bool isInvincible = (flag & INVINCIBLE) != 0;

// 무적이거나 변이 상태인가?
bool isImmuneToAttack = (flag & (INVINCIBLE | POLYMORPH)) != 0;

// 특정 상태 조합인가?
bool isStunnedAndBound = (flag & (STUN | BIND)) == (STUN | BIND);

실무 활용 예제

// 권한 시스템
enum Permission {
    READ = 1 << 0,          // 0b001
    WRITE = 1 << 1,         // 0b010  
    EXECUTE = 1 << 2        // 0b100
};

unsigned int userPermission = READ | WRITE;  // 0b011

// 실행 권한 확인
if (userPermission & EXECUTE) {
    cout << "실행 가능";
} else {
    cout << "권한 없음";
}

디스어셈블리로 확인 가능한 최적화

논리 연산 최적화

// C++ 코드
bool result = (hp <= 0 && !isInvincible);

// 어셈블리 (슈도코드)
cmp hp, 0
jg skip_second_check    // hp > 0이면 두 번째 조건 스킵
cmp isInvincible, 0
je set_true
skip_second_check:
mov result, 0

비트 시프트 최적화

int x = 10;
int doubled = x * 2;    // 컴파일러가 x << 1로 변환
int quadrupled = x * 4; // 컴파일러가 x << 2로 변환

실제 사용시

비트플래그 사용 시 주의사항:

// 좋은 예: unsigned 타입 사용
unsigned int flag = 0;

// 나쁜 예: signed 타입 (부호 비트 문제)
int flag = 0;           // 최상위 비트가 부호 비트가 됨

가독성을 위한 매크로:

#define HAS_FLAG(flags, flag) ((flags & flag) != 0)
#define ADD_FLAG(flags, flag) (flags |= flag)
#define REMOVE_FLAG(flags, flag) (flags &= ~flag)

if (HAS_FLAG(playerStatus, INVINCIBLE)) {
    cout << "무적 상태";
}