나는 이것으로 정신 나간다고 생각합니다.예기치 않은 C/C++ 비트 시프트 연산자 결과
결과로 N
비트가 1로 설정된 (부호없는) 정수를 생성해야하는 코드 조각이 있습니다. 정확하게 비트 마스크가 있고 어떤 경우에는 솔리드 한 값으로 설정하고 싶습니다. .
I 다음 함수가 :
void MaskAddRange(UINT& mask, UINT first, UINT count)
{
mask |= ((1 << count) - 1) << first;
}
간단한 즉
바이너리 표현 1 << count
은 다수 011...111
제공 등으로부터 1을 감산 (제로의 개수 count
이다) 100...000
이며 우리 단지 왼쪽 다음을 first
까지 이동하십시오.
에서 다음 명백한 제한이 충족 될 때 위의 올바른 결과를 산출한다 : 그것은 는 "극단적 인"경우에 제대로 작동합니다 것을
first + count <= sizeof(UINT)*8 = 32
참고.
- 우리가
(1 << count) = 1
이count = 0
경우, 따라서((1 << count) - 1) = 0
. 최고의 비트 오버 플로우 때문에, 및 C/C++에 따라 우리가(1 << count) = 0
이count = 32
경우 - 는, 비트 시프트 연산자는 순환하지 이다 규칙. 그런 다음
((1 << count) - 1) = -1
(모든 비트가 설정 됨).
그러나, 밝혀진 바와 같이, count = 32
에 대해서는 공식이 예상대로 작동하지 않는다. 발견 된대로 :
UINT n = 32;
UINT x = 1 << n;
// the value of x is 1
또한 MSVC2005 IDE를 사용하고 있습니다.
mov eax,1
mov ecx,dword ptr [ebp-0Ch] // ecx = n
shl eax,cl // eax <<= LOBYTE(ecx)
mov dword ptr [ebp-18h],eax // n = ecx
참으로 마법 없다 : 나는 디버거에서 위의 식을 계산할 때, 그 결과는 내가 x
우리는 다음을 참조 디스어셈블러를 통해 1 lokking 있습니다의 값을 가져옵니다, 위의 라인을 통해 단계 그러나 경우 0입니다 , 컴파일러는 shl
명령어를 사용했습니다. 그렇다면 shl
은 내가해야 할 일을하지 않는 것 같습니다. CPU가이 명령을 무시하기로 결정했거나 시프트가 32를 기준으로 처리되거나 donno가 처리됩니다.
내 질문은 :
shl
/shr
지침의 올바른 행동이 무엇입니까?- 비트 시프트 명령어를 제어하는 CPU 플래그가 있습니까?
- C/C++ 표준에 따른 것입니까? 사전에
감사
편집 : 답변
감사합니다.나는 (1) shl
/shr
실제로 피연산자 modulo 32 (또는 & 0x1F)를 취급하고 (2) C/C++ 표준은 교대를 31 비트 이상으로 정의되지 않은 행동으로 취급 함을 깨달았다.
그런 다음 질문이 하나 더 있습니다. 이 극단적 인 경우를 다루기 위해 어떻게 "마스킹"표현식을 다시 작성할 수 있습니까? 분기하지 않아야합니다 (if
, ?
). 가장 단순한 표현은 무엇입니까?
"C/C++"와 같은 언어는 없습니다. 질문을 C로 태그 지정했지만 코드 스 니펫 중 하나는 C++에서만 존재하는 'UINT & mask'라는 표기법을 사용합니다. – ruakh
두 가지가 내 마음에 떠오른다 : sizeof (UINT)는 무엇이며, 또한 shl'ing 할 때 1도 UINT인지 확인하십시오. –
덧붙여서 마지막 질문에 답하기 위해 : "C/C++ 표준에 따른 것입니까?": C 표준은 "오른쪽 피연산자의 값이 음수이거나 승격 된 값의 너비보다 크거나 같으면 왼쪽 피연산자는 동작이 정의되지 않았으며 C++ 표준에 따르면 오른쪽 피연산자가 음수이거나 승격 된 왼쪽 피연산자의 비트 길이보다 크거나 같으면 동작이 정의되지 않습니다. "그래서 어느 언어에서나 시스템 그렇게 할 때 원하는 것을 무엇이든 할 수 있으며, 프로그램을 종료하거나 사기성 전자 메일을 사장에게 보낼 수 있습니다. * * *. – ruakh