C++ Chapter 3.3 : 비트끼리의 연산, 비트 플래그, 비트 마스크

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 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 이 먼저 연산된다.

>> : 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 출력

image


비트플래그, 비트마스크 사용 방법

💛 둘 다 게임 프로그래밍에 요긴하게 쓰인다.

비트플래그

비트플래그를 사용하는 이유

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의 보수 ~opt311110111 이다.
      • 아이템 3 자리만 0 인 상태.
    • items_flag 와 11110111 와의 and & 연산.
      • 0을 만나면 무조건 0이 되는 and의 특징을 이용하여
      • items_flag의 아이템3 자리 값을 0으로 만들어 잃게 만든다.
  • 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.
  • 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
  • 빨간색 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 출력

비트 플래그를 이용해 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 이 된다.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기