HOWTO · C
C의 산술 오른쪽 시프트
C의 유일한 >> 연산자가 부호 없는 값을 어떻게 이동시키는지, 그리고 음수인 부호 있는 정수를 오른쪽으로 이동시키는 것이 왜 이식성이 없는지 알아봅니다.
이 페이지의 내용
C에는 오른쪽 시프트 연산자 >>가 하나 있습니다. 이 연산자는 두 번째 피연산자가 지정한 비트 위치 수만큼 정수 값을 오른쪽으로 이동합니다. 부호 없는 정수에서는 비어 있는 상위 비트가 0으로 채워집니다. 음수인 부호 있는 정수의 결과는 C 구현마다 달라질 수 있으므로, >>가 항상 산술 오른쪽 시프트를 수행한다고 가정해서는 안 됩니다.
C의 시프트 연산자
C에는 이항 연산자 <<와 >>가 있습니다. 왼쪽에는 정수 값, 오른쪽에는 시프트 횟수를 지정합니다. value >> count 식은 value를 count개의 비트 위치만큼 오른쪽으로 이동하고, value << count 식은 왼쪽으로 이동합니다.
예를 들어 value >> 1은 한 비트 위치만큼 이동합니다. 연산 전에 두 피연산자 모두 정수 승격을 거치므로, 실제로 시프트되는 값의 형식과 값은 좁은 정수형 객체에 선언된 형식과 다를 수 있습니다.
C의 시프트 연산자 유형
C에는 산술 오른쪽 시프트 연산자와 논리적 오른쪽 시프트 연산자가 따로 없습니다. 연산자는 항상 >>이며, 적용되는 동작은 값의 형식과 값에 따라 달라집니다. 부호 없는 값을 오른쪽으로 시프트하면 비어 있는 상위 비트가 0으로 채워집니다. 음수가 아닌 부호 있는 값의 오른쪽 시프트 결과는 해당하는 2의 거듭제곱으로 나눈 결과와 같습니다.
음수인 부호 있는 정수의 오른쪽 시프트는 이식성이 없습니다. 구현마다 처리 방식이 다를 수 있으므로, 부호 비트를 복사한다고 해서 이식 가능한 코드에서도 그렇게 된다고 보장할 수 없습니다. 따라서 산술 오른쪽 시프트라는 표현은 일부 구현에서 볼 수 있는 동작을 가리키며, C의 별도 연산자를 뜻하지 않습니다. 프로그램이 그 동작에 의존한다면 컴파일러와 대상 플랫폼에 관한 가정을 문서화하세요.
다음 예제를 살펴보세요.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
#include <stdio.h>
int main(void) {
unsigned int value = 15u;
int signed_value = -15;
printf("unsigned: %u >> 1 = %u\n", value, value >> 1);
printf("signed value: %d\n", signed_value);
return 0;
}
unsigned: 15 >> 1 = 7
signed value: -15
예제 출력에는 unsigned: 15 >> 1 = 7과 signed value: -15가 표시됩니다. 이 레이블은 바이트 동일성을 위해 영어로 그대로 표시됩니다. 부호 없는 식 15u >> 1의 결과는 unsigned int의 적합한 비트 너비에서 모두 7입니다. 프로그램은 음수인 부호 있는 값을 출력하지만 그 값에 오른쪽 시프트를 적용하지 않으므로, 해당 값의 오른쪽 시프트 결과에 대해서는 아무 주장도 하지 않습니다.
2의 거듭제곱으로 나누는 것과의 비유에는 한계가 있습니다. 부호 없는 값과 음수가 아닌 부호 있는 값에는 유용하지만, 음수인 부호 있는 값의 오른쪽 시프트를 이식 가능하게 만들지는 않습니다. 또한 시프트 횟수는 0 이상이어야 하며, 정수 승격 후 왼쪽 피연산자의 비트 너비보다 작아야 합니다. 그렇지 않으면 동작은 정의되지 않습니다. 왼쪽 시프트가 항상 2의 거듭제곱을 곱하는 것과 같다고 가정하지 마세요. 부호 있는 정수에서 오버플로가 발생하면 그 연산의 동작도 정의되지 않을 수 있습니다.
결론
C에는 >> 연산자가 하나 있습니다. 부호 없는 값의 오른쪽 시프트는 비어 있는 비트를 0으로 채우는 동작이 정의되어 있지만, 음수인 부호 있는 정수를 시프트한 결과는 이식성이 없습니다. 비트 단위의 오른쪽 시프트에서 예측 가능한 결과가 필요하다면 부호 없는 값을 사용하고, 구현 요구사항이 명시적으로 허용하지 않는 한 특정한 부호 있는 값의 동작에 의존하지 마세요.