비트 연산자에 대한 이해가 없다면, 아래 내용을 이해하기 힘들 수 있습니다.
컴퓨터는 10진수를 계산하고, 다른 언어들을 인식하고 계산하는 것처럼 보일 때가 많지만, 결국은 주어진 데이터를 모두 2진수로 인식한다.
전기신호가 0이면 off
, 1이면 on
인 것을 확인하여 연산하는 것이다.
이와 같이 boolean
또한, true
는 1과 같고, false
는 0과 같다.
우리가 프로그램을 작성할 때, 상태를 확인하기 위해 플래그를 boolean(또는 int)
변수로 생성하여 on, off
를 확인하는 경우가 많은데, 플래그가 많이 필요할 때 모든 플래그를 변수로 생성하여 하나하나 확인하는 방식은 코드가 길어질 뿐더러, 더 많은 연산을 수행해야한다.
하지만, 컴퓨터가 계산하는 방식과 같이 비트로 플래그를 처리하면 int
형 변수(클러스터 환경 32bits
) 하나로 32개의 플래그
를 처리할 수 있다.
(char
형 변수 하나로 8개 플래그
)
개요에서 비트 연산을 통해 플래그를 사용하는 이유를 알았으니, 이제 어떻게 사용해야 하는 지 알아보자!
(32비트를 다 적으면 길기 때문에, 8비트인 char
형 예시로 보자)
우선, 모든 플래그
가 false
인 상태로 변수를 선언한다.
char flag = 0; // 8개의 플래그가 모두 false인 상태로 선언
위에서 선언한 flag
변수의 비트를 왼쪽부터 각각 7에서 0번 플래그라고 하자.
그리고 0, 1, 2, 3 플래그를 기상, 아침, 점심, 저녁식사 여부라고 할 때,
일어나서 점심만 먹었다면 0번, 2번 플래그를 켜줘야 한다.
켜는 방식은 아래와 같다.
// 플래그 |= 켜줘야 하는 비트의 값(2의 (비트 위치) 제곱)
// | (bit OR 연산) - 각 위치에 비트를 확인하여 둘 중 하나라도 1이면 1으로 만듦
flag |= 4; // 2의 2제곱 (2번 플래그) / or 연산으로 2번 비트를 1으로
flag |= 1; // 2의 0제곱 (0번 플래그) / or 연산으로 0번 비트를 1으로
flag |= 4
flag |= 1
까지 연산하면, flag
는 아래 사진과 같은 상태가 된다.플래그를 사용하는 이유는 어떤 상태가 on, off인지 확인하기 위함이다!
위에서 0~3번 플래그에 각각의 행위를 부여했으니 아래 코드로 확인해보자.
// 플래그 & 확인하는 비트의 값(1, 2, 4, 8...)
// & (bit AND 연산) - 각 위치의 비트를 확인하여 둘 다 1일 때, 1로 만듦
if (flag & 1) printf("나 일어났어!"); // & 연산으로 0번 비트가 1인지 확인
if (flag & 2) printf("나 이미 아침 먹었어."); // 1번 비트가 1인지 확인
if (flag & 4) printf("나 이미 점심 먹었어."); // 2번 비트가 1인지 확인
if (flag & 8) printf("나 이미 저녁 먹었어."); // 3번 비트가 1인지 확인
// "나 일어났어!"
// "나 이미 점심 먹었어."
flag & 4
flag & 4
연산 결과가 4
임으로 true
로 인식된다.flag & 8
의 경우, flag
의 3번 플래그가 0임으로 연산 결과 0 false
을 반환한다.오늘 하루를 돌아보던 중, 내가 오늘 점심을 먹은 것이 아닌 어제 점심을 먹었다는 사실을 기억했다.
그러면 2번 플래그
를 꺼줘야 할텐데, 어떻게 끌 수 있을까?
비트 연산으로 플래그
를 false
로 만들어 주는 코드를 확인해보자
// 플래그 &= ~꺼야할 비트의 값
// ~ (bit NOT 연산) 모든 비트를 반대로(0을 1로, 1을 0으로) 만듦
flag &= ~4;
// ~ 연산으로 2번 비트(4) 빼고 모두 1로 만들고,
// &= 연산으로 2번 비트를 제외한 모든 비트는 유지
// 2번 비트는 꺼준다.(0으로 만든다.)
사실 비트를 켜고, 끄는 기능은 말 그대로 켜고, 끄기는 하지만 반전의 개념이 아니다.
비트를 켤 때, 0을 1로 만들기도 하지만 1을 1로 그대로 두기도 한다.
끌 때도 마찬가지로, 1을 0으로 만들기도 하지만 0을 0으로 그대로 둔다.
점심을 먹으면 2번 플래그
가 1이 되지만, 또 먹어도 1로 그대로 있는 것이다.
점심을 안먹은 상태에서 먹으면 플래그
를 1로 만들지만,
점심을 먹었는데(플래그가 1일 때,) 또 먹으면 플래그
를 0으로 만드는 방식을 알아보자.
// 플래그 ^= 반전시킬 비트 값
// ^ (bit XOR 연산) 서로 다른 비트일 때, 1로 연산한다.(서로 같으면 0)
flag ^= 4;
특히, XOR 연산은 다양한 방식으로 사용할 수 있으니 고민하면서 사용하면 좋을 것 같다!
다른 사람이 사용하는 걸 볼 때는 마법과 같아 보였던 비트 연산
+ 비트 연산
을 통한 플래그 관리를 알아보았다.
실무에서는 유지보수 어려움, 모르는 개발자도 많음 등의 이유로 사용하기 힘들겠지만,
42서울 과제를 할 때, 알고리즘을 풀 때 유용하게 사용할 수 있을 것 같다.
혹여나 마지막까지 읽은 분이 있다면, 그 분께도 도움이 되길 바란다. 🙂