5-2-라.쉬프트 연산자

쉬프트(Shift) 연산자는 비트들을 지정한 수만큼 좌우로 이동시킨다. >> 연산자는 오른쪽으로 비트를 이동시키며 << 연산자는 왼쪽으로 비트를 이동시킨다. 연산자의 모양이 이동 방향의 화살표와 비슷하게 되어 있어 직관적이다. a << 1은 a를 1비트 왼쪽으로 이동시키며 a << 3은 3비트 왼쪽으로 이동시킨다. 두 개의 피연산자를 취하는 이항 연산자인데 좌변은 보통 정수형 변수가 오고 우변은 정수 상수가 온다. 물론 양변이 모두 변수(a << b)일 수도 있고 양변이 모두 상수(1 << 3)일 수도 있다.

동작은 간단하지만 상당히 고급 연산자이며 응용 가치가 많다. a가 0x59라 할 때 a >> 1연산이 어떻게 동작하는지 그림으로 살펴보자. 그림에서 제일 왼쪽 비트를 b0라고 하며 제일 오른쪽 비트를 b7이라고 한다.

b7은 b6으로 이동하고 b6은 b5로 이동하여 모든 비트들이 한칸씩 오른쪽으로 이사를 갔다. 단, 제일 오른쪽에 있는 b0는 갈 곳이 없으므로 쫒겨나서 사라지고 제일 왼쪽 비트 b7은 0으로 채워진다. << 연산자는 반대로 비트들을 왼쪽으로 이동시킨다.

이번에는 제일 왼쪽에 있는 b7이 밀려나고 b0는 0으로 채워진다. 연산의 동작이 아주 간단하기 때문에 쉽게 이해가 갈 것이다. 그렇다면 이 연산을 어디다 써 먹을 수 있을지 연구해 보자. 다음은 조합형 한글 "강"자에 대한 코드를 가지고 있는 변수 a의 비트 구조이다. 상용 조합형 코드표를 찾아 보면 초성 ㄱ은 00010(2)이고 중성 ㅏ는 00011(3)이고 종성 ㅇ의 코드는 10111(0x17)로 되어 있으며 이 코드들이 5비트씩 분할되어 16비트를 구성한다.

이런 구조에서 a의 초성이 ㄱ인지 조사하고 싶다면 어떻게 해야 할까? 변수 하나에 초성, 중성, 종성이 모두 들어있기 때문에 if (a==값) 식으로 단순히 비교해서는 초성값만 비교할 수가 없다. a값에서 다른 코드는 제외하고 초성만 쏙 빼내야 한다. 이럴 때는 앞에서 배운 &연산자를 사용하여 초성 이외의 나머지 코드를 모두 마스크 오프시켜 0으로 만든다.

초성 부분만 남겨야 하므로 마스크는 0x7c00을 사용했다. 이렇게 되면 중성, 종성 비트는 모두 0이 된다. 남은 값 0x0800은 초성 코드만 가진 값인데 ㄱ의 코드인 00010과는 자리가 맞지 않기 때문에 직접 비교를 할 수 없다. 그래서 이 초성 비트를 오른쪽으로 10번 쉬프트시킨다.

이렇게 하면 a의 초성 코드만 남게 되고 자리도 맞추었으므로 원하는 초성 코드와 직접 비교할 수 있다. 결과 코드는 다음과 같다.

 

if (((a & 0x7c00) >> 10) == 2)

 

a를 0x7c00과 & 연산하여 마스크 오프시키고 10번 오른쪽으로 민 후 이 값이 ㄱ의 코드인 2인지 비교했다. 반대로 초성만 특정한 값으로 바꿀 때는 어떻게 할지 생각해 보기 바란다. 쉬프트 연산은 고속의 그래픽 처리가 필요할 때 비디오 램을 직접 액세스하기 위해서도 많이 사용된다. 비디오 램에 들어 있는 이미지를 쉬프트하면 스크롤될 것이다.

쉬프트 연산의 피연산자는 주로 부호없는 정수형이다. 실수형은 당연히 안된다. 부호있는 정수형은 가능은 하지만 이 경우 동작이 조금 달라진다. 최상위에 있는 부호 비트는 쉬프트 대상에서 제외되는데 부호는 값이 아니기 때문에 유지하는 것이 옳다. 부호있는 정수에 대한 쉬프트 연산은 권장되지 않으며 실제로 의미를 가지는 경우도 드물다.