C++ Chapter 3.3 : 비트끼리의 연산, 비트 플래그, 비트 마스크
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
비트끼리의 연산 Bitwise Operators
<<
,>>
,~
,&
,|
,^
- #include <bitset>
std::bitset<4>(x)
- x값을 4자리의 이진수 비트로 출력 하게끔 해준다. (cout 스트림에 흘림)
- 십진수를 이진수 비트로 표현해준다
- Bitwise 연산은 메모리를 의미있게, 빠르게 계산하기 위하여 사용한다.
- 그냥 곱하기, 나누기 연산하는거보다 left shift, right shift 해주는게 더 빠름
- 비트 연산은
unsigned
를 사용한다.
unsigned int a = 0b1100;
unsigned int b = 0b0110;
cout << a ; // 12 출력
cout << b ; // 6 츨력
#include <bitset>
unsigned int a = 3;
cout << std::bitset<4>(a) << endl; // 0011 출력 ( 3의 4자리 이진수 )
shift연산자 >>
<<
<<
: left shift
- 0011
<<
2 → 2칸 왼쪽 - 0011
<<
2 == 1100 왼쪽으로 2칸씩 옮겨지고 빈 곳은 0으로 채우기 - A
<<
N 은 곧 A × 2^n 곱하기 하는 것과 마찬가지다. 따라서left shift 연산
으로 곱하기 연산을 하면 더 빠르다 ! - 0011 = 십진수로 3,
-
0011 « 2 = 1100 = 십진수로 12 ( = 3 × 2^2 )
#include <bitset> unsigned int b = a << 1; // a가 왼쪽으로 1칸 옮겨진 결과가 b에 저장 됨. b = 6 cout << std::bitset<4>(b) << endl; // 0110 출력
- a = 3 =
0011
- a « 1 =
0110
= 6 >>
<<
연산자는=
보다 우선순위가 높다.- 따리서
a << 1
이 먼저 연산된다.
- a = 3 =
>>
: right shift
- 1011
>>
2 → 2칸 오른쪽 - 1011
>>
2 == 0010 오른쪽으로 2칸씩 옮겨지고 빈 곳은 0으로 채우기 - A
>>
N 은 곧 A ÷ 2^n 나누기 하는 것과 마찬가지다. 따라서rightshift 연산
으로 나누기 연산을 하면 더 빠르다 ! - 1011 = 십진수로 11
- 1011 » 2 = 0010 = 십진수로 2 ( = 11 / 2^2 = 11 / 4 몫 )
보수 연산자 ~
~
연산자는 비트에 보수를 취한다.- ~1100 = 0011
비트 논리연산자 &
|
^
- 2개의 비트 피연산자의 논리 관계를 따진다.
- 결과값은 boolean 타입
- 각 자리 끼리 연산한다.
&
: and- a &= b → a = a & b
|
: or-
a = b → a = a b
-
^
: xor ⭐ 같으면 false, 다르면 true- a ^= b → a = a ^ b
unsigned int a = 0b1100;
unsigned int b = 0b0110;
cout << std::bitset<4>(a & b) << endl; // 0100 출력
cout << std::bitset<4>(a | b) << endl; // 1110 출력
cout << std::bitset<4>(a ^ b) << endl; // 1010 출력
비트플래그, 비트마스크 사용 방법
💛 둘 다 게임 프로그래밍에 요긴하게 쓰인다.
비트플래그
비트플래그를 사용하는 이유
boolean has_item1 = true;
boolean has_item2 = false;
boolean has_item3 = true;
boolean has_item4 = true;
boolean has_item5 = false;
.....
-
boolean has_item1 = true, boolean has_item2 = false , boolean has_item3 = true, …..
→ 불 편 ! ! !
- 예를들어 게임 프로그래밍에서 아이템1 가졌는지 아이템 2 가졌는지 체크 하려면
- 아이템 개수가 64개라면 64개의 boolean 변수가 필요하다.
- 각 아이템을 가졌는지 체크해서 실행하는 함수가 있다면 파라미터도 64개나 받아야하는 꼴이 된다. for문을 64번 돌리던지…
→ 비트에 아이템 유무 플래그를 각각 표시하면 간편하다.
비트 플래그
- unsigned int 변수 하나가 32bit를 가지고 있으니
- 각 비트 자리를 아이템들에 대치시켜 아이템 유무를 비트 자리 값 1, 0 으로 판단하면 된다.
- 이렇게 하면 unsined int 변수 하나로 *32개의 아이템 유무 정보*를 표현 가능
- unsigned int 변수 2개만으로 64개 아이템도 표현할 수 있겠네
#include <bitset>
/* 이렇게 각 아이템 유무 상태들만 정의 */
const unsigned char opt0 = 1 << 0; // 00000001 아이템 0을 가진 상태 정의
const unsigned char opt1 = 1 << 1; // 00000010 아이템 1을 가진 상태 정의
const unsigned char opt2 = 1 << 2; // 00000100 아이템 2을 가진 상태 정의
const unsigned char opt3 = 1 << 3; // 00001000 아이템 3을 가진 상태 정의
const unsigned char opt3 = 1 << 4; // 00010000 아이템 4을 가진 상태 정의
const unsigned char opt3 = 1 << 5; // 00100000 아이템 5을 가진 상태 정의
const unsigned char opt3 = 1 << 6; // 01000000 아이템 6을 가진 상태 정의
const unsigned char opt3 = 1 << 7; // 10000000 아이템 7을 가진 상태 정의
cout << bitset<8>(opt0) << endl; // 00000001 출력
cout << bitset<8>(opt1) << endl; // 00000010 출력
cout << bitset<8>(opt2) << endl; // 00000100 출력
cout << bitset<8>(opt3) << endl; // 00001000 출력
unsigned char **items_flag** = 0; // 현재는 00000000 로 초기화. 아이템 없는 상태.
- opt0, opt1, opt2, opt3 각각 아이템 0,1,2,3 을 가진 상태 비트를 정의한 것이다. const.
1 << n
연산 사용해 정의. 각 아이템 자리를 나타내는거라 한자리만 1.- 정의 해놓은 opt 들의 조합으로
items_flag
를 조작해 아이템 가진 상태들을 만들고 수정해나갈 것이다. - 정의들만 해놓고
items_flag
변수 하나로 아이템 보유 상태를 표현할 것.items_flag
비트의 각 자리는 아이템0~7의 유무를 표현한다.
아이템을 얻었을 때. |=
unsigned char **items_flag** = 0; // 현재는 00000000 로 초기화. 아이템 없는 상태.
cout << "No item " << bitset<8>(items_flag) << endl; // 00000000 출력
items_flag **|=** opt0; // 아이템 0 을 얻었다.
cout << "item 0 obtained " << bitset<8>(items_flag) << endl; // 00000001 출력
items_flag **|=** (opt2 | opt3); // 아이템 2와 3 을 얻었다. (opt2 | opt3) -> 000001100
cout << "item 2, 3 obtained " << bitset<8>(items_flag) << endl; // 00001101 출력
- 아이템을 얻었을 떄
|=
로 아이템 상태를 합집합 한다.
아이템을 잃었을 때. &= ~
// items_flag 는 현재 00001101 상태
items_flag **&= ~** opt3; // 아이템 3 을 잃었다.
cout << "item 3 losted " << bitset<8>(items_flag) << endl; // 0000**0**101 출력
- items_flag &= ~ opt3;
- opt3의 보수
~opt3
은11110111
이다.- 아이템 3 자리만 0 인 상태.
- items_flag 와
11110111
와의 and&
연산.- 0을 만나면 무조건 0이 되는 and의 특징을 이용하여
- items_flag의 아이템3 자리 값을 0으로 만들어 잃게 만든다.
- opt3의 보수
- 00001101 에서 아이템 3을 잃고 00000101 가 되었다.
해당 아이템 소유 상태를 확인하고 싶을 땐 &
& =
이 아닌 그냥&
- 고로 items_flag 값을 변화시키는건 아니다. 단지 확인만.
if (items_flag & opt1) //itmes_flag의 아이템 1 자리가 0 이면 false가 나와 가지고 있지 않다고 판명
cout << "Has item1" << endl;
else
cout << "Not have item1" << endl; // 이게 출력될 것.
- 현재 items_flag 값 → 00000101
- opt1 → 00000010
- if (items_flag
&
opt1)- itmes_flag의 아이템 1 자리가 0 이면 결과값이 00000000로 false가 나와서
- 아이템 1을 가지고 있지 않다고 판명된다.
- 아이템 1의 자리는 0이므로 else에 걸릴 것.
- itmes_flag의 아이템 1 자리가 1 이면 결과값이 00000010로 true가 나와서
- 아이템 1을 가지고 있다고 판명된다.
- 0 이 아닌 모든 수는 true.
- itmes_flag의 아이템 1 자리가 0 이면 결과값이 00000000로 false가 나와서
if
( ( items_flag&
opt2 )&&
! ( items_flag & opt1 ) )- 응용) 아이템 2는 가지고 있고 아이템 1은 가지고 있지 않다면.
해당 아이템 유무 상태를 뒤바꿔주려면 XOR ^ =
- 있다면 없게 바꾸고 없다면 있게 바꾸기
// items_flag 는 현재 00000101 상태
if ( ( items_flag & opt2 ) && ! ( items_flag & opt1 ) )
{
items_flag **^=** opt1; // 000001**1**1
items_flag **^=** opt2; // 00000**01**1
}
cout << bitset<8>(items_flag) << endl; // 00000011 출력
- 현재 items_flag 값 → 00000101
if
( ( items_flag&
opt2 )&&
! ( items_flag & opt1 ) )- 아이템 2는 가지고 있고 아이템 1은 가지고 있지 않다면.
- items_flag ^= opt1;
- 아이템1 값이 0이므로 1로 바뀐다. → 00000111
- 아이템2 값이 1이므로 0로 바뀐ㄷ. → 00000011
비트 마스크
- 색 Color를 표현할 땐 보통 1 byte(=8bit) 3개로 표현한다. ( 총 24 bit )
- 16진수 하나의 수로 4bit 표현 가능
- 0 ~ 9 → 0000 ~ 1001
- A → 1010
- B → 1011
- C → 1100
- D → 1101
- E → 1110
- F → 1111
- FF → 11111111
- 16진수 6개로 색 표현 가능
- 16진수 2자리 수로 각각 빨간색, 초록색, 파란색 표현.
- 이 3개의 색 조합으로 색을 표현한다.
- 0x RR GG BB
- 16진수 하나의 수로 4bit 표현 가능
- 빨간색 0xFF0000
- 초록색 0x00FF00
- 파란색 0x0000FF
- 0xDAA520 → 11011010 10100101 00100000
- 빨간색이 DA 정도, 초로색이 A5 정도, 파란색이 20 정도로 섞여있는 색이라는 뜻
비트 플래그를 이용해 blue 값 추출하기
const unsigned int blue_mask = 0x0000FF;
unsigned int pixel_color = 0xDAA520;
cout << std::bitset<32>(blue_mask) << endl;
cout << std::bitset<32>(pixel_color) << endl;
unsigned char blue = pixel_color & blue_mask;
cout << "blue " << bitset<8>(blue) << endl;
- cout « std::bitset<32>(blue_mask) « endl;
- 00000000 00000000 00000000 11111111 출력
- cout « std::bitset<32>(pixel_color) « endl;
- 00000000 11011010 10100101 00100000 출력
- cout « “blue “ « bitset<8>(blue) « endl;
- pixel_color
&
blue_mask;- blue값 자리에 해당되는 것들만 추출
- 00100000 출력
- pixel_color
비트 플래그를 이용해 green 값 추출하기
- green값은 8~16 번째 자리에 위치한다.
- 중간에 위치하므로 8자리로 추출시 →
right shift연산
필요
const unsigned int green_mask = 0x00FF00;
unsigned int pixel_color = 0xDAA520;
unsigned char green = pixel_color & green_mask;
green = (pixel_color & green_mask) >> 8
cout << "green " << bitset<8>(green) << endl;
- unsigned char green = pixel_color & green_mask;
- 00000000 00000000 10100101 00000000 값이 된다.
- green = (pixel_color & green_mask) » 8
- 8자리로 출력할거라서 오른쪽으로 8칸 밀어준다.
- 00000000 00000000 00000000 10100101
- cout « “green “ « bitset<8>(green) « endl;
- 10100101 출력
연습문제
unsigned char option_viewed = 0x01; // 00000001 기사를 클릭했을 때 상태
unsigned char option_edited = 0x02; // 00000010 기사를 수정 했을 때 상태
unsigned char option_liked = 0x04; // 00000100 기사의 좋아요 했을 때 상태
unsigned char option_shared = 0x08; // 00001000 기사를 공유했을 때 상태
unsigned char option_deleted = 0x80; // 10000000 기사를 삭제했을 때 상태
unsigned char my_article_flag = 0;
1. 기사를 보았을 때
my_article_flag |= option_viewed
00000001
이 된다.
2. 기사 좋아요 눌렀을 때
my_article_flag |= option_liked
000000101
이 된다.
3. 기사 좋아요를 다시 눌렀을 때
my_article_flag ^= option_liked
000000001
이 된다.
4. 본 기사만 삭제할 때
if (my_article_flag & option_viewed) // 본 기사로 상태가 저장되어 있다면
my_article_flag |= option_deleted // 삭제한다.
100000001
이 된다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글남기기