无法理解 C 代码中的移位运算符行为
看一下这个示例 C 代码(提取一个测试用例作为示例):
main() {
unsigned long a, b;
int c;
c = 32;
a = 0xffffffff << 32;
b = 0xffffffff << c;
printf ("a=%x, b=%x\n", a, b);
}
打印: a=0, b=ffffffff
我无法理解为什么 b 不为零,就像 a 一样。我在 Microsoft C 和 GCC 上对此进行了测试。
更新:我修复了愚蠢的拼写错误(当然应该是 << c 而不是 << b)。但我的问题仍然存在,例如结果仍然相同。
Look at this sample C code (extracted a test case as an example):
main() {
unsigned long a, b;
int c;
c = 32;
a = 0xffffffff << 32;
b = 0xffffffff << c;
printf ("a=%x, b=%x\n", a, b);
}
Prints: a=0, b=ffffffff
I cannot understand why b is not zero, just like a. I tested this on Microsoft C and GCC.
Update: I fixed the stupid typo (should have been << c and not << b of course). But my question still stands, e.g. the result is still the same.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
在此处使用
b
之前,您从未将其初始化为任何内容:因此它可以是任何内容。 (我认为您实际上是想按
c
进行移位。)除此之外:
主要问题是按数据类型中的位数或更多位数进行移位是未定义的行为。< br>
因此,如果文字
0xffffffff
是一个 32 位整数,则该行:不是由标准定义的。 (请参阅评论。)
您还应该收到编译器警告:
You never initialized
b
to anything before you use it here:So it can be anything. (I think you actually meant to shift by
c
.)That aside:
The main issue is that shifting by the # of bits in the datatype or more is undefined behavior.
So if the literal
0xffffffff
is a 32-bit integer, then the line:is not defined by the standard. (See comments.)
You should also be getting a compiler warning:
在 C 中,左移超过提升操作数的宽度是未定义的行为。
我假设在下面的示例中
int
和unsigned int
类型是 32 位。无后缀的十六进制整数常量的类型是相应列表中可以表示其值的第一个:
int
、unsigned int
、long
,无符号长整型
,长整型
,无符号长整型
。所以这里
0xFFFFFFFF
的类型是unsigned int
并且0xFFFFFFFF << 32
是未定义的行为。In C it is undefined behavior to left shift more than the width of the promoted operand.
I assume in the examples below
int
andunsigned int
types are 32-bit.The type of an unsuffixed hexadecimal integer constant is the first in the corresponding list in which its value can be represented:
int
,unsigned int
,long
,unsigned long
,long long
,unsigned long long
.So here
0xFFFFFFFF
is of typeunsigned int
and0xFFFFFFFF << 32
is undefined behavior.没有人提到这个问题的另一个有趣的方面。在 x86 计算机上,移位量使用模 32,因此移位 32 实际上是移位 0。在 ARM 计算机上,移位值(如果来自寄存器)不会更改,因此任何移位值 32 或更大的结果将始终是目标寄存器中的 0。
具体来说,在您的问题中,编译器对常量值很聪明,因此它将 (0xffffffff << 32) 转换为正确的值 (0)。在第二个示例中,编译器无法直接计算移位量,因为它来自变量。在 Intel 机器上执行时,左移 32 会导致左移 0。
Nobody has mentioned another interesting aspect of the problem. On x86 computers, the shift amount is used modulus 32, so a shift by 32 will really be a shift by 0. On ARM machines, the shift value (if coming from a register) is not altered, so any shift value of 32 or greater will always result in 0 in the destination register.
Specifically in your problem, the compiler is smart about the constant values, so it turns (0xffffffff << 32) into the correct value (0). In your second example, the compiler can't directly calculate the shift amount since it's coming from a variable. When executed on an Intel machine, shifting left by 32 results in a shift left by 0.
我敢打赌,在修正你的拼写错误后,你会得到 2 个零。原因如下:
如果您的程序(我打赌是)编译为版本,则您已打开优化。这意味着编译器将使用所谓的“常量折叠”,而 CPU 甚至不会执行这些移位。在底层代码中,会将 2 个零常量压入堆栈并调用 printf。这些操作(移位)的结果本质上将成为程序中的常量。所以不存在未定义的行为等 - 你的 a 和 b 变成常量值,你会得到类似的结果:
I bet that after fixing your typo you get 2 zeros as a result. Here is why:
If your program is (I bet is) compiled as release you have optimisations turned on. This means that compiler will use so called "constant folding" and CPU will not even perform these shifts. Under the hood code will have 2 zero constants pushed onto the stack and invoke printf. Results of these operations (shifts) will essentially become constants in the program. So there are no undefined behaviours etc. - your a and b becomes constant values and you'll get sth like:
你有
b = 0xffffffff << b;
,但也许您的意思是b = 0xffffffff << c;
You have
b = 0xffffffff << b;
, but perhaps you meantb = 0xffffffff << c;
为什么它应该为零?您可以将其移动一个垃圾数(
b
的第一个值),该数字可以为 0。Why should it be zero? You shift it by a garbage number (the first value of
b
) that can be 0.您没有初始化 B,因此在程序运行时它将具有该特定内存位置的任何(随机)值。
You didn't initialize B, so it's going to have whatever (random) value is at at that particular memory location at the time the program runs.
当您到达
b = 0xffffffff << 行时b;
,b的值还没有被赋值。看起来它的值为零,使得指令b = 0xffffffff << b;
。这解释了给定的答案。By the time you get to the line
b = 0xffffffff << b;
, the value of b has not been assigned. It looks like its value is zero, making the instructionb = 0xffffffff << b;
. Which expalins the given answer.好的做法是轮班后 AND。在这种情况下,你清楚地知道会发生什么。
例如:
Good practice is AND after shift. In this case you know stritcly whats happen.
Examply: