비트 연산자
- 비트 단위로 연산이 진행될 때 사용하는 연산자
- 비트 쉬프트(bit shift) : << , >>
화살표가 가리키는 방향으로 민다.
비트를 왼쪽, 오른쪽으로 민다!!
ex)
unsigned char byte = 1;
byte << 1
왼쪽으로 한칸씩 민다!!라는 의미
- 2진수에서 한칸 올라간다는 것은 2배 증가한다는 뜻이다. (10진수를 예로 들어서 이해해보기)
(비트가 한자리 올라간다는 것은 2배가 된다는 의미이다.)
0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
위의 2진수를 10진수로 표현하면 10이다 (2의 3제곱 + 2이니까)
이게 한칸씩 왼쪽으로 이동하면
0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
10의 2배인 20이다. (2의 4네제곱 + 2의 2제곱이니까)
즉 각각이 한칸씩 올라갔기 때문에 전체의 값이 2배가 된 것이다!!
byte <<= 1;
//서로 같은 의미
byte = byte << 1
값이 증가하는 경우 예시
byte <<= 2; //값이 4배 늘어남
byte <<= 3; //값이 8배 늘어남
값이 감소하는 경우 예시
byte >>= 1; //값이 1/2 감소,절반으로 줄어든다
근데 만약 byte가 13(홀수)이라면??
unsigned char byte = 13;
byte >>= 1;
>>1이 2로 나누는거라면 13을 2로 나누면 나머지는 어떻게 되는가...?
→ 나머지는 오른쪽으로 밀려 소실되므로 2로 나눈 몫이라고 생각하면 된다!!!
자세히 살펴보자.
2진수에서 홀수는 2의 0승 자리에 1이 올 것이다
1 |
그럼 당연히 13도 2의 0승, 즉 일의 자리에 1이 오겠지. 13을 2진수로 표현하면 다음과 같다.
0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
여기서 한칸씩 오른쪽으로 밀면 가장 오른쪽에 있던 1이 소실된다.

따라서 오른쪽으로 한칸 이동한 것은 2로 나눈 몫이라고 생각하면 된다!
정리해보면
왼쪽으로 이동시 : 2^n 배수
오른쪽으로 이동시 : 2^n으로 나눈 몫
예시
byte <<= 3; //2^3배수, 8배 증가
byte >>= 2; //2^2 나눈 몫만 남게 된다.
비트 곱, 합, xor
- 비트 곱 : &
- 비트 합 : |
- xor : ^
- 반전 : ~
비트 연산 : 비트 단위로 하나씩 연산하는 것, 자리대로, 자릿수 맞춰서 진행함.
★ 비트 합(|) 연산
- 2개의 비트를 비트 단위로 자리대로 연산했을 때 둘 중 적어도 하나 1일 경우 1이 나오고, 둘 다 0일 경우 0이 나온다.
1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
|
1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
★ 비트 곱(&) 연산
- 2개의 비트를 비트 단위로 자릿수대로 연산했을 때 둘 다 1일 경우 1이 나오고, 적어도 하나가 0일 경우 0이 된다.
1 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
&
1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 |
1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
★ 비트 XOR(^) 연산
- 2개의 비트가 같으면 0, 다르면 1
1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
^
0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 |
1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
★ 비트 반전(~)
- 1은 0으로, 0은 1로 반전
1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
~
0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
define
-비트 단위 연산은 많이 사용한다. 게임에서 많이 사용함. 잘 기억하고 있어야 한다.
# : 전처리 단계에서 수행될 수 있는 명령어. 모든 컴파일 과정 중에 제일 먼저 수행된다.
#define : 내가 지정한 구문을 특정 숫자로 치환해준다. 나중에 함수같은 느낌으로 쓸 수 있음.
#define HUNGRY 1
...
int iStatus = HUNGRY; //iStatus는 1이 된다. 그리고 컴파일이 진행됨
- 장점
여러가지 상태가 중첩됨.
1) 가독성
- 배고픔 수치를 20 이런식으로 적어놓는 것보다 HUNGRY라는 것으로 직관적으로 코드의 의미를 알 수 있다.
2) 코드 유지 보수 측면에서 좋음.
- 값이 변화할 때 하나하나 사용된 그 코드를 찾아다니면서 수정할 필요가 없다. define에서 정의된 값만 변경해주면 된다.
int에서 한번에 표현할 수 있는 상태가 32가지 (32bit니까)
... | ... | ... | ... | ... | ... | ... | 피곤한 상태 (0 or 1) |
목마름 상태 (0 or 1) |
배고픔 상태 (0 or 1) |
32비트 변수로는 한번에 표현할 수 있는 상태가 32가지인 것이다.
(1을 준비해 놓았다가 딱 끼워넣는 것! - 상태의 값을 넣다 뺐다 넣다 뺐다)
각 자리 비트에 대응하는 숫자를 특정 하나의 상태로 본다!!!!
목마른 상태는 비트의 2번째 칸에 위치하므로 2로 저장
(각 한자리에 맡는 숫자여야 하니까 2로, 2 부분만 1이고 나머지 자리는 다 0으로 하기 위해서..)
피곤한 상태는 비트의 3번째 칸에 위치하므로 4로 저장
#define HUNGRY 1
#define THIRSTY 2
#define TIRED 4
아무 상태도 없을 때
unsigned int iStatus = 0;
배가 고픈 상태
unsigned int iStatus = 0;
int iStatus |= HUNGRY;
배도 고픈데 목도 마른 상태
(상태를 점점 합쳐주면 된다) - 비트 합 사용
unsigned int iStatus = 0;
iStatus |= HUNGRY;
iStauts |= THIRSTY;
THIRSTY 상태를 넣어주면서 iStatus의 다른 상태를 건드리지 않는다. 각 자리에 대해서만 비트 연산을 하기 때문에
원래 상태는 원래 상태대로 나올 것이고, THIRSTY에 대해서만 추가되는 것!!!
넣어주고 싶은 상태만 그 자리에 넣어주면 되는 것!!!
비트로 보자.
아무 상태 아닐 때 (32bit)
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
배고픈 상태 추가 (32bit)
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
|
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
자 여기서 이제 목마름 상태도 추가해보자
(배고프고 목마른 상태)
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
|
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
각 자리에 대해서만 상태가 추가되었다가 제거되었다 하는 것이지 다른 상태의 영향을 주지 않는다!
상태를 넣어주었으면 물어볼 수도 있다!
지금 배고픈 상태인가?
지금 목마른 상태인가? 등등..
각 상태가 어떠한지 물어볼 수 있다. (상태값 체크)
if(iStatus & THIRSTY){
...
}
비트 곱 연산자는 비교하는 두 비트가 모두 1일 때만 1이다.
따라서 내가 궁금한 상태를 제외한 나머지가 어떠하든간에
THIRSTY는 2^1자리 제외하고 나머지가 다 0이기 때문에 비트곱 연산을 하면 내가 궁금한 상태 부분을 제외하고는 다 0인 환경이 된다.
따라서 내가 궁금한 상태의 비트 연산 결과 값만 조건문에 반영할 수 있다.
내가 지정한 자리만 비트 1인지 물어보는 것!!!!!
(1)
(현재 상태 - 종합적 상태 다 존재: 목마름, 배고픔...)
... | ... | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 |
&
(개발자가 궁금한 상태 - 목마름 상태)
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
- 현재 캐릭터는 지금 목마른 상태이네~
(2)
(현재 캐릭터 상태 - 종합적 상태 다 존재: 목마름, 배고픔...)
... | ... | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
&
(개발자가 궁금한 상태 - 목마름 상태)
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- 현재 캐릭터는 목마르지 않은 상태네~
if(iStatus & 화상상태){
//화상 상태면 hp를 몇초에 몇칸씩 뺀다
//이런거 들어갈 수 있음
}
그러면 해당 자리만 비트를 제거하고 싶다면 어떻게 해야 할까?
캐릭터 화상상태 1 -> 화상상태 0으로 만들고 싶다면?
혹은
캐릭터 피곤한 상태 1 -> 많이 쉬었음 -> 피곤한 상태 0으로 피곤한 상태 제거하고 싶다면?
특정 자리 비트 제거
xor 연산을 사용하지 않음 → 문제가 있다
//특정 자리 비트 제거
iStauts &= ~THIRSTY;
현재 캐릭터 상태
... | ... | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 |
위에 형광표 친 부분의 상태(목마름1 -> 목마름0) 뺄거임

-THIRSTY 준비
... | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
- 비트 곱을 진행하기 전에 내가 원하는 상태(THIRSTY)를 반전시킨다.
... | ... | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
- 현재 캐릭터 상태와 ~THIRSTY 비트곱을 진행한다!
(현재 캐릭터 상태)
... | ... | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 |
&
... | ... | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
... | ... | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 |
결과 - 목마름 상태(1) - > 목마르지 않은 상태(0)으로 변경됨

캐릭터의 목마름 상태가 0이였어도 그대로 0으로 내려온다. 1로 바뀌지 않는다(XOR의 문제였음)
즉 목마른 상태가 1이였으면 0으로 바뀌고 0이였으면 그대로 0으로 유지됨!
//특정 자리 비트 제거
iStauts &= ~THIRSTY;
이 구문은 외우고 "특정 비트가 빠졌구나" 이렇게 바로 알아볼 수 있으면 된다!!!
#define HUNGRY 1
#define THIRSTY 2
#define THIRED 4
#define FIRE 8
#define COLD 16
코드로 상태를 나열할 때는 보통 16진수로 쓴다.
c++에서는 16진수 앞에 0x를 붙인다.
#define HUNGRY 0x001
#define THIRSTY 0x002
#define THIRED 0x004
#define FIRE 0x008
#define COLD 0x010 //16은 16진수에서 10
#define POSITION1 0x020 //32는 16의 두묶음이니까
#define POSITION2 0x040
#define POSITION3 0x080
#define POSITION4 0x100
#define POSITION5 0x200
#define POSITION6 0x400
#define POSITION7 0x800
//16진수는 1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
아래 강의로 보고 개념 정리한 것입니다
https://www.youtube.com/watch?v=hjAqjGYmQnA&list=PL4SIC1d_ab-aOxWPucn31NHkQvNPHK1D1&index=12