비트 연산으로 플래그 처리하기 🪄

Donghun Ha·2022년 1월 7일
1

비트 연산자에 대한 이해가 없다면, 아래 내용을 이해하기 힘들 수 있습니다.

👀 개요

컴퓨터는 10진수를 계산하고, 다른 언어들을 인식하고 계산하는 것처럼 보일 때가 많지만, 결국은 주어진 데이터를 모두 2진수로 인식한다.

전기신호가 0이면 off, 1이면 on인 것을 확인하여 연산하는 것이다.
이와 같이 boolean 또한, true는 1과 같고, false는 0과 같다.

우리가 프로그램을 작성할 때, 상태를 확인하기 위해 플래그를 boolean(또는 int)변수로 생성하여 on, off를 확인하는 경우가 많은데, 플래그가 많이 필요할 때 모든 플래그를 변수로 생성하여 하나하나 확인하는 방식은 코드가 길어질 뿐더러, 더 많은 연산을 수행해야한다.

하지만, 컴퓨터가 계산하는 방식과 같이 비트로 플래그를 처리하면 int형 변수(클러스터 환경 32bits) 하나로 32개의 플래그를 처리할 수 있다.
(char 형 변수 하나로 8개 플래그)

🪄 플래그 처리

개요에서 비트 연산을 통해 플래그를 사용하는 이유를 알았으니, 이제 어떻게 사용해야 하는 지 알아보자!

1️⃣ char형 변수 선언

(32비트를 다 적으면 길기 때문에, 8비트인 char 형 예시로 보자)

우선, 모든 플래그false인 상태로 변수를 선언한다.

  • 코드
    char flag = 0; // 8개의 플래그가 모두 false인 상태로 선언
  • 비트

2️⃣ 비트(플래그) 켜기

위에서 선언한 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는 아래 사진과 같은 상태가 된다.

3️⃣ 비트(플래그) on, off 확인

플래그를 사용하는 이유는 어떤 상태가 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 을 반환한다.

4️⃣ 비트(플래그) 끄기

오늘 하루를 돌아보던 중, 내가 오늘 점심을 먹은 것이 아닌 어제 점심을 먹었다는 사실을 기억했다.
그러면 2번 플래그를 꺼줘야 할텐데, 어떻게 끌 수 있을까?

비트 연산으로 플래그false로 만들어 주는 코드를 확인해보자

  • 코드
    // 플래그 &= ~꺼야할 비트의 값
    // ~ (bit NOT 연산) 모든 비트를 반대로(0을 1로, 1을 0으로) 만듦
    flag &= ~4;
    
    // ~ 연산으로 2번 비트(4) 빼고 모두 1로 만들고,
    // &= 연산으로 2번 비트를 제외한 모든 비트는 유지
    // 2번 비트는 꺼준다.(0으로 만든다.)
  • 비트

5️⃣ 특정 비트(플래그) 반전

사실 비트를 켜고, 끄는 기능은 말 그대로 켜고, 끄기는 하지만 반전의 개념이 아니다.
비트를 켤 때, 0을 1로 만들기도 하지만 1을 1로 그대로 두기도 한다.
끌 때도 마찬가지로, 1을 0으로 만들기도 하지만 0을 0으로 그대로 둔다.

점심을 먹으면 2번 플래그가 1이 되지만, 또 먹어도 1로 그대로 있는 것이다.

점심을 안먹은 상태에서 먹으면 플래그를 1로 만들지만,
점심을 먹었는데(플래그가 1일 때,) 또 먹으면 플래그를 0으로 만드는 방식을 알아보자.

  • 코드
    // 플래그 ^= 반전시킬 비트 값
    // ^ (bit XOR 연산) 서로 다른 비트일 때, 1로 연산한다.(서로 같으면 0)
    
    flag ^= 4;
  • 비트

특히, XOR 연산은 다양한 방식으로 사용할 수 있으니 고민하면서 사용하면 좋을 것 같다!


🌌 마무리

다른 사람이 사용하는 걸 볼 때는 마법과 같아 보였던 비트 연산 + 비트 연산을 통한 플래그 관리를 알아보았다.

실무에서는 유지보수 어려움, 모르는 개발자도 많음 등의 이유로 사용하기 힘들겠지만,
42서울 과제를 할 때, 알고리즘을 풀 때 유용하게 사용할 수 있을 것 같다.

혹여나 마지막까지 읽은 분이 있다면, 그 분께도 도움이 되길 바란다. 🙂

profile
Corca Backend Engineer, dha

0개의 댓글