본문 바로가기
C │ C++ │ C#/C++

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

by Pokaa 2023. 8. 17.
728x90
728x90
SMALL

비트끼리의 연산 Bitwise Operators

 

  •  << >> 
  • #include <bitset>
    ο  std::bitset<4>(x) 
        ■ x값을 4자리의 이진수 비트로 출력 하게끔 해준다. $($cout 스트림에 흘림$)$
        ■ 십진수를 이진수 비트로 표현해준다
  • Bitwise 연산은 메모리를 의미있게, 빠르게 계산하기 위하여 사용한다.
    그냥 곱하기, 나누기 연산하는거보다 left shiftright 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으로 채우기
  • <<  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으로 채우기
  • >>  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으로 만들어 잃게 만든다.
  • 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  이 된다.
728x90
300x250
LIST