【发布时间】:2012-04-09 06:32:32
【问题描述】:
我觉得我要疯了。
我有一段代码需要创建一个(无符号)整数,并将N 后续位设置为 1。确切地说,我有一个位掩码,在某些情况下,我想将其设置为实心范围。
我有以下功能:
void MaskAddRange(UINT& mask, UINT first, UINT count)
{
mask |= ((1 << count) - 1) << first;
}
简单来说: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
此外,我使用的是 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
确实没有什么神奇之处,编译器只是使用了shl 指令。然后似乎shl 没有做我期望它应该做的事情。要么 CPU 决定忽略这条指令,要么以 32 为模处理移位,或者什么都不做。
我的问题是:
-
shl/shr指令的正确行为是什么? - 是否有控制移位指令的 CPU 标志?
- 这符合 C/C++ 标准吗?
提前致谢
编辑:
感谢您的回答。我意识到(1)shl/shr 确实将操作数模 32(或 & 0x1F)和(2)C/C++ 标准将移位超过 31 位视为未定义行为。
那么我还有一个问题。我怎样才能重写我的“屏蔽”表达式来涵盖这种极端情况。它应该没有分支(if,?)。最简单的表达方式是什么?
【问题讨论】:
-
没有“C/C++”这样的语言。您已将问题标记为 C,但您的代码之一 sn-ps 使用符号
UINT& mask,该符号仅存在于 C++ 中。 -
我想到了两件事:sizeof(UINT) 是什么,并确保 1 在 shl'ing 时也是 UINT。
-
顺便回答你的最后一个问题:“这符合 C/C++ 标准吗?”:C 标准说,“如果右操作数的值为负数或大于或等于提升的左操作数的宽度,行为未定义”,并且 C++ 标准说,如果右操作数为负数,或者大于或等于提升的左操作数的位长度,则行为未定义。所以在任何一种语言中,当你这样做时,系统绝对可以做它想做的任何事情;它可以终止程序,或者向你的老板发送愤怒的电子邮件——任何事情。
标签: c bit-manipulation