>`) C에서 32비트씩 이동할 때 예상치 못한 결과가 발생합니까? " />
오른쪽 시프트 연산자의 예기치 않은 동작(1 >> 32)
프로그래밍 영역에서는 오른쪽 시프트 연산자(>>)가 일반적으로 사용됩니다. 특히 정수를 2의 거듭제곱으로 나누기 위해 비트 연산을 수행합니다. 그러나 다음 C 코드에서 볼 수 있듯이 더 큰 값으로 이동할 때 특이한 동작이 발생할 수 있습니다.
int foo(int a, int b) {
return a >> b;
}
int bar(uint64_t a, int b) {
return a >> b;
}
int main() {
std::cout > 32: " > 32) > (int)32: " > (int)32) 놀랍게도 이 프로그램의 출력에는 예상치 못한 결과가 나타납니다.
foo(1, 32): 1 // Should be 0
bar(1, 32): 0
1 >> 32: 0
(int)1 >> (int)32: 0
이 결과의 근거는 CPU와 컴파일러의 내부 작동에 있습니다.
foo() 함수의 동작
foo() 함수에서는 캐스트 없이 시프트 연산이 수행되어 CPU가 논리적 오른쪽 시프트를 수행하게 됩니다. 많은 아키텍처에서 논리적 오른쪽 시프트는 a >> (b % 32)로 구현되어 b의 상위 비트를 효과적으로 무시합니다. 따라서 foo(1, 32)의 결과는 1 >> (32 % 32)이며, 이는 1 >> 0으로 평가되어 1을 산출합니다.
64비트 정수로 변환하는 것이 왜 중요한가요?
bar() 함수에는 64비트 부호 없는 정수가 제공되어 b(32)가 피연산자의 비트 수(64)보다 작기 때문에 결과가 0이 되도록 보장합니다. ). 그러나 b가 64로 변경되면 결과는 예측할 수 없게 되며 여전히 1이 나올 수 있습니다.
컴파일러 최적화
1 >> 32 및 (int )1 >> (int)32, 컴파일러는 컴파일 타임에 이러한 상수 표현식을 최적화합니다. 표준은 개수가 음수이거나 피연산자 길이보다 크거나 같은 오른쪽 시프트에 대해 정의되지 않은 동작을 지정합니다. 32가 피연산자의 길이를 초과하므로 컴파일러는 결과를 확인할 수 없으며 안전한 대체 값으로 0을 출력합니다.
CPU 관련 동작
오른쪽 시프트 구현 작업은 CPU마다 다를 수 있습니다. x86/x86-64 아키텍처에서 논리적 오른쪽 시프트는 모드에 따라 사실상 a >>(b % 32 또는 64)입니다. 그러나 ARM 프로세서에서 오른쪽 시프트 연산은 32보다 크거나 같은 시프트에 대해 0을 보장합니다.
결론
오른쪽 시프트 연산자로 작업할 때는 다음이 필수적입니다. 특히 시프트 수가 피연산자 길이를 초과하는 경우 잠재적인 정의되지 않은 동작을 고려합니다. 64비트 정수와 같은 더 넓은 정수 유형으로 변환하면 다양한 CPU 및 컴파일러에서 일관된 결과를 보장할 수 있습니다.
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3