意外的C/C++按位移位运算符结果

val*_*ldo 6 c bit-manipulation

我想我会疯了.

我有一段代码需要创建一个(无符号)整数,其N后续位设置为1.确切地说,我有一个位掩码,在某些情况下,我想将它设置为一个实心的rnage.

我有以下功能:

void MaskAddRange(UINT& mask, UINT first, UINT count)
{
    mask |= ((1 << count) - 1) << first;
}
Run Code Online (Sandbox Code Playgroud)

简单来说:1 << count在二进制表示中100...000(零的数量是count),从这样的数字中减去1给出011...111,然后我们只是左移它first.

当满足以下明显限制时,上述结果应产生正确的结果:

first + count <= sizeof(UINT)*8 = 32

请注意,它也应该适用于"极端"情况.

  • 如果count = 0我们有(1 << count) = 1,因此((1 << count) - 1) = 0.
  • 如果count = 32我们有(1 << count) = 0,因为前导位溢出,并且根据C/C++规则,按位移位运算符不是循环的.然后((1 << count) - 1) = -1(所有位设置).

然而,事实证明,count = 32公式不能按预期工作.如发现:

UINT n = 32;
UINT x = 1 << n;
// the value of x is 1
Run Code Online (Sandbox Code Playgroud)

而且,我正在使用MSVC2005 IDE.当我在调试器中评估上面的表达式时,结果为0.但是当我跳过上面的行时,x得到值1.通过反汇编程序看到以下内容:

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
Run Code Online (Sandbox Code Playgroud)

确实没有魔法,编译器只使用了shl指令.然后似乎shl没有做我预期应该做的事情.CPU决定忽略此指令,或者将移位视为模32,或者不知道什么.

我的问题是:

  • shl/ shr指令的正确行为是什么?
  • 是否有CPU标志控制位移指令?
  • 这是根据C/C++标准吗?

提前致谢

编辑:

谢谢你的回答.我已经意识到(1)shl/ shr确实对待操作数模32(或&0x1F)和(2)C/C++标准将移位超过31位视为未定义的行为.

然后我还有一个问题.我怎样才能重写我的"掩蔽"表达来覆盖这种极端情况.它应该没有分支(if,?).什么是最简单的表达方式?

oua*_*uah 12

1U << 32当类型unsigned int为32位宽时,在C和C++中是未定义的行为.

(C11,6.5.7p3)"如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义"

(C++ 11,5.8p1)"如果右操作数为负数,或大于或等于提升左操作数的位长度,则行为未定义."