C语言中的位移操作出现意外行为

3

我目前正试图从一个名为 addr 的地址中,使用一个名为 mask 的32位掩码来提取一些位,并将其存储到另一个变量 result 中,方法如下:

int addr = 7;
int x = 0;
uint32_t mask = 0xFFFFFFFF;
result = addr & (mask >> (32 - x));

当x=0时,我希望结果为0,在在线位移计算器上已经确认过了。但是在C代码中,result的值是1。为什么会这样呢?


4
行为未定义。http://port70.net/~nsz/c/c11/n1570.html#6.5.7p3 如果右操作数的值为负数或大于等于升级后的左操作数宽度,则行为未定义。 - Eugene Sh.
addr的类型是什么? - VillageTech
2
@hyde 为什么?没有它也很清楚啊。不过应该是重复的。 - Eugene Sh.
@EugeneSh。你是对的! - Jean-François Fabre
如果你想知道为什么它是未定义行为,请考虑硬件。例如,在x86(和可能许多其他处理器)上,“目标操作数可以是寄存器或内存位置。计数操作数可以是立即值或寄存器CL。计数被掩码为5位,这将计数范围限制为0到31。”因此,当硬件掩码计数值时,您不能进行32位移位,因为这与移动0位相同。 - Jerry Jeremiah
显示剩余2条评论
2个回答

2
您正在执行非法的位移操作。
当按左操作数的位大小进行移位时,移位值大于或等于左操作数位数会导致未定义行为。这在C标准的第6.5.7p3节中有文档记录:

对每个操作数执行整数提升。结果的类型是所提升的左操作数的类型。如果右操作数的值为负数或大于等于提升后的左操作数的宽度,则行为未定义。

这意味着您需要检查x的值,如果为0,则只需使用0作为位掩码。
int x = 0;
uint32_t mask = 0xFFFFFFFF;
...
if (x == 0) {
    result = 0;
} else {
    result = addr & (mask >> (32 - x));
}

@Clifford 如果 x 等于1,则我们有 mask >> 31 == 0xffffffff >> 31 == 1。 - dbush
我需要更多的睡眠(评论已删除)!另一种选择是 addr & ((mask >> (31 - x)) >> 1) ,然后检查 x==0 是不必要的(我想 - 仍然需要睡眠!) - Clifford

1

根据C语言标准(6.5.7位移运算符):

3 对每个操作数进行整数提升。结果的类型是被提升的左操作数的类型。如果右操作数的值为负或大于等于被提升的左操作数的宽度,则行为未定义


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接