Menu

비트 연산(Bit manipulation)

믹스인(mixin)을 가장 잘 활용하여 D 언어의 기능을 충분히 끌어내는 경우의 예로 비트 연산을 들 수 있습니다.

간단한 연산(Simple bit manipulation)

D 언어에서 제공하는 기본 비트 연산자들입니다.

  • & 비트 연산용 and
  • | 비트 연산용 or
  • ~ 비트 연산용 negative
  • << 비트 연산용 산술(원래 값의 부호가 유지됩니다) 좌향 shift
  • >> 비트 연산용 산술(원래 값의 부호가 유지됩니다) 우향 shift
  • >>> 비트 연산용 부호 없는 우향 shift

실질적 예시(A practical example)

비트 연산을 쓰는 대부분의 경우는 어떤 값의 특정 비트가 어떤 상태인지 확인하는 일인데, 내장 라이브러리인 core.bitop.bt 를 사용하면 처리할 수 있습니다.

그러나, 이번 섹션에서는 번거롭지만 라이브러리 없이 직접 다뤄보겠습니다.

enum posA = 1;
enum maskA = (1 << posA);
bool getFieldA()
{
    return _data & maskA;
}

위의 예제는 단 한자리의 비트만 확인할 수 있었지만, 일반화하여 연속된 자릿수의 비트를 읽어봅시다.

읽은 부분만을 추출해야 하기 때문에 자릿수 구간을 나타내줄 특별한 마스크(mask)가 필요합니다. 이 마스크는 읽지 않아야할 부분을 0으로 만들어줄 것입니다.

그리고 원하는 값만 읽기 위해 결과물에서 우측에 남아있는 불필요한 비트를 제거할 필요가 있습니다. (좌측은 0으로 채워집니다)

enum posA = 1;
enum lenA = 3;
enum maskA = (1 << lenA) - 1; // ...0111 인데, & 연산을 하면 _data에서 1로된 부분만 1로 남습니다
uint getFieldA()
{
    return (_data >> posA) & maskA;
}

역으로, _data 의 특정 비트 자릿수 구간에 원하는 비트를 설정하는 건 마스크를 반전시켜 비트를 설정할 부분을 찾아내는 것입니다.

void setFieldA(bool b);
{
    return (_data & ~maskAWrite) | ((b << aPos) & maskAWrite);
}

도와줘요 std.bitmanip ! (std.bitmanip to the rescue)

자기만의 특별한 비트 연산 함수를 정의하고 이용하는 건 재밌는 일이고, D 언어는 이를 위한 충분한 기능을 제공하고 있습니다.

다만, 이렇게 비트 연산 처리하는 코드를 단순히 복사해서 붙여넣어 이용하는 건 코드 유지보수 측면에서 불리할 뿐더라 오류 발생의 원인이 됩니다.

std.bitmanip 표준 라이브러리를 이용해 유지보수하기 편리하면서도 코드 가독성을 높인, 하지만 성능에서 전혀 손해보지 않는 비트 연산 코드를 작성해보십시오.

실습 화면을 보십시오. BitVectorstruct 로 정의되어있고 다룰 때도 struct 처럼 다루면 되지만 실제론 내부에서 비트필드(bitfield)를 이용한 비트 연산이 이루어지고 있습니다.

std.bitmanipcore.bitop 에는 메모리가 극단적으로 부족해 비트 단위로 메모리를 자주 다뤄야할 응용 프로그램을 위한 유용한 도구들을 제공하고 있습니다.

메모리 공간 상의 자리채움과 배치(Padding and alignment)

OS 커널이 다루는 메모리 최소 단위(size_t.sizeof)보다 더 작은 공간을 차지하는 값을 다루는 경우에, D 언어 컴파일러는 OS 커널에서 처리할 수 있게 원본 값에 더미(dummy) 값을 더 붙여 메모리 최소 단위를 맞춥니다. 만약 4바이트(32비트)를 한 단위로 사용하는 OS라면, bool, byte, char 와 같은 타입을 다룰 때 이런 일이 발생하게 됩니다.

이렇게 되면 자칫 소중한 메모리가 낭비될 수 있으므로 효율적으로 잘 배치될 수 있도록 신경써주는 편이 좋습니다. 예제에서 struct BadVector 의 경우를 자세히 보십시오.

더 살펴보기

rdmd playground.d