C 为什么将一个变量移位超过它的宽度(以位为单位)就等于零?
这个问题受到StackOverflow其他问题的启发。今天,在浏览StackOverflow时,我遇到了一个问题,即用大于等于该变量的宽度(以位为单位)的值k对变量进行位移位。这意味着将32位整数移位32位或更多位 从这些问题中可以明显看出,如果我们试图将一个数移位k位,即>=变量的位宽度,则只取最低有效的log2k位。对于32位整数,最低有效5位被屏蔽,并作为移位量 所以一般来说,如果w=变量的宽度(以位为单位), x>>k变成x>>k%w 对于int,这是x>>k%32 计数被屏蔽为5位,这将计数范围限制为0到31 所以我写了一个小程序来观察理论上应该产生的行为。我已经在评论中写下了由此产生的移位量%32C 为什么将一个变量移位超过它的宽度(以位为单位)就等于零?,c,undefined-behavior,bit-shift,C,Undefined Behavior,Bit Shift,这个问题受到StackOverflow其他问题的启发。今天,在浏览StackOverflow时,我遇到了一个问题,即用大于等于该变量的宽度(以位为单位)的值k对变量进行位移位。这意味着将32位整数移位32位或更多位 从这些问题中可以明显看出,如果我们试图将一个数移位k位,即>=变量的位宽度,则只取最低有效的log2k位。对于32位整数,最低有效5位被屏蔽,并作为移位量 所以一般来说,如果w=变量的宽度(以位为单位), x>>k变成x>>k%w 对于int,这是x>>k%32 计数被屏蔽为5位,这
#include <stdio.h>
#include <stdlib.h>
#define PRINT_INT_HEX(x) printf("%s\t%#.8x\n", #x, x);
int main(void)
{
printf("==============================\n");
printf("Testing x << k, x >> k, where k >= w\n");
int lval = 0xFEDCBA98 << 32;
//int lval = 0xFEDCBA98 << 0;
int aval = 0xFEDCBA89 >> 36;
//int aval = 0xFEDCBA89 >> 4;
unsigned uval = 0xFEDCBA89 >> 40;
//unsigned uval = 0xFEDCBA89 >> 8;
PRINT_INT_HEX(lval)
PRINT_INT_HEX(aval)
PRINT_INT_HEX(uval)
putchar('\n');
return EXIT_SUCCESS;
}
并且输出与移位指令的预期行为不匹配
==============================
Testing x << k, x >> k, where k >= w
lval 00000000
aval 00000000
uval 00000000
=====================================================================
实际上,我对Java有点困惑。在C/C++中,将一个int移位一个大于位宽度的位数可能会减少k%w,但C标准不能保证这一点。没有规则规定这种行为应该一直发生。这是未定义的行为
然而,Java就是这样。这是Java编程语言的一条规则
链接问题明确指出,如果移位量大于被移位类型的位宽度,则在使用不可移植或错误的程序结构或错误数据时,本标准将调用该类型,该行为定义为行为,本国际标准对此不作任何要求 当您调用未定义的行为时,任何事情都可能发生。程序可能会崩溃,可能会输出奇怪的结果,或者看起来工作正常。此外,如果在同一编译器上使用不同的编译器或不同的优化设置,则未定义行为的显示方式可能会发生变化 C标准规定了第6.5.7p3节中关于位移位运算符的以下内容: 对每个操作数执行整数提升。这个 结果的类型是提升的左操作数的类型。如果 右操作数的值为负或大于 或等于提升后的左操作数的宽度,则行为为 未定义
在这种情况下,编译器可能会如您所建议的那样,将移位量减少到位宽度的模,或者将其视为在数学上移位该量,从而导致所有位都为0。因为标准没有指定行为,所以两者都是有效的结果。未定义的一个原因是8086(原始x86)没有屏蔽移位计数中的任何位。相反,它实际上执行了移位,每个位置使用一个时钟滴答声 英特尔随后意识到,允许移位指令使用255+时钟滴答声可能不是一个好主意。例如,他们可能考虑了最大中断响应时间 从我的旧80286手册: 为了减少最大执行时间,iAPX 286不允许移位计数大于31。如果尝试移位计数大于31,则仅使用移位计数的底部五位。iAPX 86使用移位计数的所有8位 对于PC/XT和PC/AT上的完全相同的程序,这会给出不同的结果 那么语言标准应该说什么呢
Java通过不使用底层硬件解决了这个问题。C反而选择说效果不清楚。好吧,我不知道语言规范会说什么,但我总是认为移位是从一边去掉一点,在另一边插入一个0。因此,使用这种逻辑,移位的位数大于或等于变量的宽度,最终肯定会产生全零。您链接的问题表明该行为未定义,因此所有赌注均已取消。您链接的问题引用了标准:如果右操作数为负,或大于或等于提升后的左操作数的位长度,则该行为未定义。如果36%32==4,则应将变量移位4字节,你从哪里得到减少通过模移位的位置数的想法?在你提到的两个公认的答案中,我都看不到这一点。Java与任何东西有什么关系?问题的最后一部分似乎偏离了主题。要使80386与80286类似,它应该在移位32位值时查看底部的六位,因为精确地按字大小移位有时是一种有用的角情况。IIRC,x186确实屏蔽了移位计数 而x86没有。一种在运行时检测差异的方法。