- "본 내용은 Inflearn에 Rookies 강사님의 강의로 학습하고 있는 내용입니다."-
>>>>> 이전 내용 : 비교연산과 논리연산
비트 연산과 비트 플래그
전체코드
/*비트 연산과 비트플래그*/
#include <iostream>
using namespace std;
// 주제 : 데이터 연산
// 비트 플래그
unsigned char flag; // 부호를 없애야 >> 하더라도 부호비트가 딸려오지 않는다.
int main()
{
#pragma region 비트연산
// 언제 필요한가? (많이 없긴하다)
// 종종 비트 단위의 조작이 필요할 때
// 게임서버 ID를 만들거나. 꾸겨서 넣다보니 비트 조작이 필요
// - 대표적으로 BitFlag
// 비트연산자 어떤 숫자에 대해 비트단위로 세밀하게 조작할 때.
// 특정 비트를 끄거나 켜거나 하는
// ~ bitwise not
// : 단일 숫자의 모든 비트를 대상으로 0은 1, 1은 0으로 (비트를 뒤집어준다)
// & bitwise and
// : 두 숫자의 모든 비트 쌍을 대상으로, (즉 두 비트 사이에 값으로 판단 1 0 = 0, 1 1 = 1 같은 식)
// | bitwise or
// : 두 숫자 사이에 or문 ( 0 0 = 0, 1 0 = 1, 1 1 = 1 과 같은형태)
// ^ bitwise xor
// : 두 숫자의 모든 비트 쌍을 대상으로 xor를 한다 ( 0 0 = 0 , 1 0 = 1, 1 1 = 0, 같으면 싫다 같은)
// 숫자를 두번 xor 하면 원복된다 (암호화 할 때 유용?)
// << 비트 좌측 이동
// : 비트열을 N만큼 왼쪽으로 이동
// 왼쪽의 넘치는 N개의 비트는 버린다. 새로 생성되는 N개의
// *2를 할 때 자주 보이는 패턴 (비트당 하나씩 2의 제곱 되기 때문)
// >> 비트 우측 이동
// : 좌측보다 까다로움 : 비트열을 N만큼 오른쪽으로 이동
// 오른쪽에 넘치는 N개의 비트는 버린다.
// 왼쪽 생성되는 N개 비트는? 부호가 있냐 없느냐에 따라 차이가 난다.
// - 부호 비트가 존재할 경우 부호 비트를 따라간다 (부호 있는 정수라면 이 부분을 유의하자)
// - 아니라면 0이다.
// 실습
// 예시 ) 이상상태 비트플래그가 아닌 bool값으로 사용할 수도 있다. 그러나 만약 상태가 여러가지 30몇개면 엄청 많아질 것이다.
// 0b0000 [무적][변이][스턴][공중부양]
// 첫번째 비트 0001 공중부양, 0010 스턴, 0100 변이, 1000 무적, 1100 무적+변이
// 각 비트에 의미를 부여할 수 있다.
// bool 노가다를 막을 수 있다.
/*무적상태*/
flag = (1 << 3);
/* 변이 상태를 추가 (무적 + 변이) */
// flag |= 4; // 이것도 가능하겠지만.
flag |= (1 << 2);
// 무적인지 확인하고 싶다? (다른 상태는 관심 없음)
// bitmask : 필요 없는 부분은 가린다.
bool bInvisible = ((flag & (1 << 3)) != 0);
// 무적이거나 스턴 상태인지 확인하고 싶다면?
bool bStunOrInvicible = ((flag & 0b1010) != 0);
#pragma endregion
}
잘 사용하진 않으나 종종 사용한다. 알아두는 편이 좋다. 이런 것이 있다 정도
- 종종 비트 단위의 조작이 필요할 때 사용된다.
- 게임 서버 ID를 만들거나, 꾸겨서 넣다 보니 비트 조작이 필요할 때가 있다.
- 대표적으로 - BitFlag
비트 연산자 : 어떤 숫자에 대해 비트 단위로 세밀하게 조작할 때 사용되며, 특정 비트를 끄거나 켜거나 하는 기능이다.
Bitwise not
- 단일 숫자의 모든 비트를 대상으로 0은 1, 1은 0으로 ( 즉, 비트를 뒤집어 준다)
Bitwise and
- 두 숫자의 모든 비트 쌍을 대상으로 (즉, 두 비트 사이에 값으로 판단한다)
- 1 0 = 0, 1 1 = 1, 과 같은 식으로 작동 된다고 보면된다.
Bitwise or
- 두 숫자 사이에 or문
- 0 0 = 0, 1 0 = 1, 1 1 = 1 형식으로 작동된다.
^ bitwise XOR
- 두 숫자의 모든 비트 쌍을 대상으로 xor한다
- 0 0 = 0 , 1 0 = 1, 1 1 = 0, 같으면 싫다 (난 다른게 좋아 같은 느낌)
아래 코드 같이 숫자를 두번 xor하면 원복 된다. (암호화 할 때 유용한 느낌이다)
int a = 1;
int b = 123;
a = a ^ b;
a = a ^ b;
// 다시 원상 복귀 된다. 1이였던 숫자가 다시 튀어나온다는 것
<< bitshift 좌측
만약 0110을 맨앞에 작업을 해주고 싶을 때 코드상에 6이라는 숫자을 만들고 시프트 연산을 이용해서 옮겨줘서 만들어주는 것 해당 만들어주고자 하는 숫자가 너무 크기 때문에 작업을 어떻게 활용할 수 있을 것 같다.
>> bitshift 우측
예를 들어 unsigned가 아닌 경우 부호 비트가 있는 숫자이고,하필 음수였다는 값이라면
음수 부호가 붙은 unsigned 형태가 아닌 값은 우측 시프트 연산을 해도 비트에 1은 그대로 이다.
즉 음수 부호가 있는 값은 그대로 유지된 채 적용된다.
Unsigned 할 때에 그냥 앞에 부호가 없는 0 비트라면 그냥 무조건 0 으로 채워진다.
비트 단위 작업을 할 때 반드시 그것을 unsigned 형태로 만들어야 정신건강에 좋다.
실습
바로 넣어서 사용할 수도 있지만.
우리가 만약 엄청나게 많은 상태를 처리해야 할 때 문제가 생기는게 있는데 바로 넣게 되면 저렇게 위와 같이 저렇게 복잡한 숫자를 다 기억하면서 까지 처리해줄 수는 없다.
그래서 바로 넣기보다 조립해서 넣는 것이 좋다.
무적상태
unsigned char flag;
flag = 8; ---- 1
...
flag = (1 << 3); ---- 2
- 2번째 방법 처럼 하면 8 값으로 컴파일러가 대체해준다.
- 두번째 방법이 의미가 좀더 잘 전달될 수 있다.
예시로 무적이라는 것을 만약 3번으로 셋팅한다 했을 때 const int INVISIBLE = 3; 이런식으로 상수로 쓸 수 있을 것이다.
무적인지 아닌지 확인할 때
flag & (1 << 3) : 무적에 대한 정보만 추출 (1 << 3) , flag에 해당 마스크를 씌어준다.
최종 결과물이 0인지 아닌지 확인하면 무적인지 아닌지 확인 해 볼 수 있다.
bool bInvisible = ((flag & (1 << 3)) != 0);
예시로
위처럼 수식이 입력되었을 때 나머지 & 연산 비트마스크 연산에 의해 나머지는 0이 될 것이다.
: 가면을 씌어서 원하는 비트를 추출했다 라는 의미로 보면 된다.
무적이거나 스턴 상태인지 확인하고 싶다면
bool bStunOrInvicible = ((flag & 0b1010) != 0);
이런식으로 해서 현재 들어온 flag의 값과 1010이 무적과 스턴 상태 이니까. 여기서 0이 아닌 값을 추출할 수 있다.
위 처럼 하드코딩이 아닌 아래와 같이 코드를 작성할 수 도 있다.
bool bMask = (1 << 3) | (1 << 1);
bool bStunOrInvicible = ((flag & bMask) != 0);
실행
조사식에서 확인해볼 수 있다.
디버깅 모드에서 처음 확인할 부분에 중단점 해두고 flag를 조사식으로 확인해보면 0으로 되어있는데 (1 << 3)이 되면 8번으로 값이 셋팅된 것을 볼 수 있다.
flag = (1 << 3);
01001856 mov byte ptr [flag (0100A1B8h)],8
- 강제로 그냥 대입하는 것을 볼 수 있다. flag에 그래서 8로하든 비트연산자를 쓰든 차이가 없다는 것이다.
다음으로 flag |= (1 << 2); 여기에서 다음으로 2번 이동한 곳에 값을 1로 바꿔줘서 변이상태를 추가해준다.
/* 변이 상태를 추가 (무적 + 변이) */
// flag |= 4; // 이것도 가능하겠지만.
flag |= (1 << 2);
0100185D movzx eax,byte ptr [flag (0100A1B8h)]
01001864 or eax,4
01001867 mov byte ptr [flag (0100A1B8h)],al
- Assembly에서도 or 처럼 작성해서 작동된다.
무적인지 확인하는 부분은 bool로 해당 부분이 0인지 아닌지 확인 한다. 1이니까. true가 추출 될 것이다.
// 무적인지 확인하고 싶다? (다른 상태는 관심 없음)
// bitmask : 필요 없는 부분은 가린다.
bool bInvisible = ((flag & (1 << 3)) != 0);
0100186C movzx eax,byte ptr [flag (0100A1B8h)]
01001873 and eax,8
01001876 je __$EncStackInitStart+48h (01001884h)
01001878 mov dword ptr [ebp-0DCh],1
01001882 jmp __$EncStackInitStart+52h (0100188Eh)
01001884 mov dword ptr [ebp-0DCh],0
0100188E mov cl,byte ptr [ebp-0DCh]
01001894 mov byte ptr [bInvisible],cl
- 형광색으로 다른지 체크 되는 부분이 저렇게 동작하는 것을 볼 수 있다.
무적인지 스턴인지 확인할 때도 동일하다.
// 무적이거나 스턴 상태인지 확인하고 싶다면?
bool bStunOrInvicible = ((flag & 0b1010) != 0);
01001897 movzx eax,byte ptr [flag (0100A1B8h)]
0100189E and eax,0Ah
010018A1 je __$EncStackInitStart+73h (010018AFh)
010018A3 mov dword ptr [ebp-0DCh],1
010018AD jmp __$EncStackInitStart+7Dh (010018B9h)
010018AF mov dword ptr [ebp-0DCh],0
010018B9 mov cl,byte ptr [ebp-0DCh]
010018BF mov byte ptr [bStunOrInvicible],cl
- 숫자만 0A로 바꿔진 것을 볼 수 있다. HEX 값 A 가 들어간 것을 알 수 있다.\
비트 플래그가 어려울 수도 있으나 이해만 한다면 간단한 것이다.
'🅿🆁🅾🅶🆁🅰🅼🅼🅸🅽🅶 > C_C++' 카테고리의 다른 글
[C++과 언리얼로 만드는 MMO] C++ 기초_ const와 메모리구조[Section3]{(21)} (1) | 2024.09.20 |
---|---|
[C++과 언리얼로 만드는 MMO] C++ 기초_ 비교 연산과 논리 연산[Section3]{(19)} (2) | 2024.09.15 |
[C++과 언리얼로 만드는 MMO] C++ 기초_산술연산자[Section3]{(18)} (5) | 2024.09.13 |