[TIL] C# 비트 보수 연산자 '~'

Dreamer·2024년 9월 19일

1. 오늘 배운 것

오늘은 C# 문법에서 잘 사용하지 않는 연산자에 대해 생각해 봤다.
코딩을 하면서 정말 잘 사용하지 않는 연산자들로는 시프트 연산자 와 비트 연산지 인데, 오랜만에 공부를 하면서 사용하면 어떤식으로 사용을 할까 알아보다가 velog에 남겨야겠다 싶어서 오늘 TIL로 작성하게 되었다.

2. 비트 보수 연산자란...

비트 연산자는 공식 문서에는 비트 보수 연산자 딱 하나만 있다.

int a = 5; // 이진수로 0000 0101
int result = ~a; // 이진수로 1111 1010 (보수 결과), 10진수로 -6
Console.WriteLine(result); // 출력: -6

위 코드에서 알 수 있듯이 이진수 값을 비트 보수 연산자로 변환 해주면 변수의 비트값을 반대로 바꿔버리는 기능을 한다.

3. 그러면 언제 사용하지?

  • 첫번째 예시로는 특정 비트 플래그를 토글하는데 사용할 수 있다.
int flags = 0b0000_1111; // 초기 플래그 상태

// 세 번째 비트를 반전시킴
int mask = 0b0000_0100;
flags = flags ^ mask;

Console.WriteLine(Convert.ToString(flags, toBase: 2)); // 출력: 00001011

flags변수에 00001111 라는 초기 플래그 상태로 선언되어 있는데 이것을 mask 변수의 이진수 값 00000100 라는 값으로 XOR 연산자(^)를 사용해서 3번째 비트를 토글 할 수 있게 된다. 그럼 결과적으로 flags 변수의 값은 00001011 라는 값으로 변환 된 것을 확인 할 수 있다.
이런 식의 토글방법은 게임 프로그래밍에서 유닛의 상태변화에 주로 사용된다.

  • 두번째 예시로는 권한 기능에서 특정 권한만 토글하는 방식으로 사용 할 수 있다.

흔히 웹개발에서는 잘 사용하지 않는 방식이긴 하지만 리눅스에서 사용하는 권한 방식과 유사하다.

uint permissions = 0b1111_1111; // 모든 권한을 부여한 상태라고 보자.
uint readPermissionMask = 0b0000_0001; // 읽기 권한 mask
permissions = permissions & ~readPermissionMask; // 모든 권한에서 읽기 권한만 해제한 상태
Console.WriteLine(Convert.ToString(permissions, toBase: 2)); // 출력: 11111110

permissions 변수에 11111111 값을 선언하고 readPermissionMask00000001AND 논리 연산과 비트 보수 연산을 해주면 나머지 mask의 1인 값을 제외한 나머지는 그대로 있고 1인 값만 0으로 바뀐다.
앞서 설명 했듯이 리눅스 권한 방식과 유사한 방식이다.

4. 그 밖에... (종료자)

그 밖에 비트 보수 연산자를 사용하는 곳은 finalizers(종료자)에 사용된다.
finalizer란 클래스에서 인스턴스를 생성하고 이후에 리소스가 해제 될때 (가비지 컬렉터에 의해 해제 될때) 호출되는 함수라고 생각하면 된다.

class MyClass
{
	// 종료자 선언
	~MyClass() 
    {
    	// 리소스 해제 될 때 코드
        System.Diagnostics.Trace.WriteLine("MyClass finalizer is called.");
    }
}

언뜻 보면 Dispose 패턴과 유사하긴 하지만 호출 시점이 다르다고 한다.
또한 IDisposable 과 혼용해서 사용 할 수 있다.

class ResourceHolder : IDisposable
{
    private bool disposed = false;

    // 관리되지 않는 리소스
    private IntPtr unmanagedResource;

    public ResourceHolder()
    {
        // 리소스 할당
        unmanagedResource = AllocateResource();
    }

    ~ResourceHolder()
    {
        // 종료자에서 Dispose 호출
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        // 가비지 컬렉터에서 종료자를 호출하지 않도록 설정
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            // 관리되지 않는 리소스 해제
            FreeResource(unmanagedResource);

            if (disposing)
            {
                // 관리되는 리소스 해제
            }

            disposed = true;
        }
    }
}

가끔 코딩을 하다보면 unmanagedResource 같이 관리되지 않는 리소스 같은 경우 직접 자원을 해제 해줘야하는 경우가 생긴다. 이런 경우에 class의 인스턴스의 자원을 해제 할때 종료자(~)에서 DisposeunmanagedResource의 자원을 해제 하는 것을 알 수 있다.

여기까지 보수 비트 연산자에 대해 알아 보았다.
그 외에 비트를 조작하는 시프트 연산자가 여러개 있는데 이것들은 추후 정리해 보도록하겠다.

profile
새로운 시작

0개의 댓글