HOWTO · Csharp

C#에서 비트마스크 사용

이 기사에서는 C#에서 비트마스크를 사용하는 방법에 대해 알아봅니다.

이 페이지의 내용

비트마스크는 각 비트가 서로 독립적인 플래그를 나타내는 정수입니다. 예를 들어 권한 값을 하나의 숫자로 저장하면서 읽기, 쓰기, 실행 설정을 각각 나타낼 수 있습니다. C# 비트 연산자를 사용하면 이러한 비트를 검사하고, 추출하고, 설정하고, 해제하고, 토글하고, 반전하고, 시프트할 수 있습니다. 이름이 있는 마스크를 사용하면 각 연산이 어떤 플래그에 영향을 주는지 분명해집니다.

비트마스크와 비트 연산자 이해하기

각 비트는 0 또는 1입니다. 연산자는 서로 대응하는 비트를 비교합니다. AND (&)는 두 입력이 모두 1일 때만 1을 만들고, OR (|)는 둘 중 하나라도 1이면 1을 만듭니다. XOR (^)는 두 입력이 서로 다를 때 1을 만들며, NOT (~)은 정수 피연산자의 모든 비트를 뒤집습니다. 시프트 연산자 (<< 및 >>)는 비트를 지정한 위치 수만큼 이동합니다.

연산 각 비트에 대한 의미
x & y 두 값에서 모두 설정된 비트만 유지합니다.
`x y`
x ^ y 두 값 중 정확히 하나에서 설정된 비트를 설정합니다.
~x 정수 표현의 모든 비트를 뒤집습니다.

마스크는 관심 있는 위치에는 1, 나머지 위치에는 0이 있는 값입니다. Read = 1, Write = 2처럼 이름이 있는 상수를 사용하세요. 이 값들은 서로 다른 비트 위치를 선택합니다.

&로 비트 검사 및 추출하기

플래그를 검사하려면 값과 해당 마스크를 AND 연산합니다. 결과가 0이 아니면 플래그가 설정된 것입니다. 예를 들면 if ((permissions & Read) != 0)과 같습니다. 마스크가 나머지 위치의 비트를 모두 지우기 때문에 이 방식으로 플래그를 확인할 수 있습니다. 선택한 비트를 여러 개 추출하려면 해당 위치들을 포함하는 마스크를 사용하세요. 결과에는 선택한 비트만 남습니다. 예를 들어 permissions & (Read | Write)는 읽기 및 쓰기 플래그를 함께 추출합니다.

|로 비트 설정하기

값에 플래그 마스크를 OR 연산하면 다른 비트는 유지하면서 해당 비트를 켤 수 있습니다. 복합 대입 permissions |= Write는 permissions | Write의 결과를 다시 permissions에 대입하는 축약형입니다. 이미 설정된 플래그에 다시 적용해도 설정된 상태가 유지되므로, 플래그를 켜진 상태로 보장하려는 경우에 적합합니다.

^로 비트 토글하기

마스크와 XOR 연산하면 마스크에서 1인 위치의 비트만 뒤집힙니다. 예를 들면 permissions ^= Execute와 같습니다. 설정된 비트는 해제되고, 해제된 비트는 설정됩니다. 명시적으로 상태를 토글할 때 유용하지만 플래그를 켜진 상태로 보장하는 멱등 연산은 아닙니다. 두 번 적용하면 원래 값으로 돌아갑니다.

~로 비트 반전하기

NOT은 정수 형식의 모든 비트를 반전합니다. 여기에는 애플리케이션에서 플래그로 사용하지 않는 위치도 포함됩니다. 알고 있는 플래그 하나를 해제하려면 AND와 반전된 마스크를 함께 사용하세요. 예를 들면 permissions &= ~Write와 같습니다. 나머지 플래그 위치는 바뀌지 않습니다. 반전된 값을 고정 너비 비트 문자열로 표시해야 한다면 먼저 해당 너비에 맞게 마스크를 적용하세요. ~value의 숫자 결과 자체는 표시하려는 플래그 범위로 제한되지 않습니다.

<< 및 >>로 비트 시프트하기

왼쪽 시프트는 비트를 더 높은 위치로 이동합니다. 음수가 아니고 시프트 결과가 표현 범위 안에 드는 값에서는 value << count가 2의 거듭제곱을 곱하는 효과를 냅니다. 오른쪽 시프트는 비트를 더 낮은 위치로 이동하며, 음수가 아닌 값에서는 시프트 횟수만큼 최하위 비트들을 버립니다. 부호 있는 음수 값에서 C#의 오른쪽 시프트는 부호 비트를 유지하므로 모든 오른쪽 시프트가 0을 채운다고 설명해서는 안 됩니다. 논리적 오른쪽 시프트가 필요하면 부호 없는 형식을 사용하고, 정수 형식에 맞는 시프트 횟수를 선택하세요.

시프트를 사용하면 마스크도 간결하게 만들 수 있습니다. 1 << 0, 1 << 1, 1 << 2는 작은 음이 아닌 횟수만큼 시프트해 연속된 비트 위치를 선택합니다. 애플리케이션 코드에서는 보통 이름이 있는 상수가 더 읽기 쉽습니다.

실제 예제로 플래그 조합하기

이 프로그램은 권한이 없는 상태에서 시작해 읽기와 쓰기를 설정하고, 해당 플래그를 검사하고 추출한 다음 쓰기를 해제하고 실행을 토글합니다. 출력되는 각 값은 해당 연산을 수행한 뒤의 상태를 나타냅니다. 예제에는 완전한 Main 진입점이 있으며 마스크는 십진수로 표현합니다.

다음 예제를 실행하세요.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

using System;

class Program
{
    static void Main()
    {
        const int Read = 1;
        const int Write = 2;
        const int Execute = 4;
        int permissions = 0;

        permissions |= Read | Write;
        Console.WriteLine((permissions & Read) != 0);
        Console.WriteLine(permissions & (Read | Write));

        permissions &= ~Write;
        permissions ^= Execute;
        Console.WriteLine(permissions);
    }
}

첫 번째 연산은 OR로 두 플래그를 설정합니다. 검사는 Read가 설정되어 있는지를 출력하고, 추출은 선택된 비트의 숫자 값을 출력합니다. &= ~Write로 쓰기 비트만 해제한 뒤 XOR로 실행 플래그를 토글합니다. 마스크와 저장된 값의 정수 형식이 호환되는지 확인하고, 의도한 비트 위치가 명확하도록 이름이 있는 플래그를 사용하세요.