无法理解 C 代码中的移位运算符行为

发布于 2025-01-01 08:48:55 字数 401 浏览 0 评论 0原文

看一下这个示例 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

酒与心事 2025-01-08 08:48:55

在此处使用 b 之前,您从未将其初始化为任何内容:

b = 0xffffffff << b;

因此它可以是任何内容。 (我认为您实际上是想按 c 进行移位。)


除此之外:

主要问题是按数据类型中的位数或更多位数进行移位是未定义的行为。< br>
因此,如果文字 0xffffffff 是一个 32 位整数,则该行:

a = 0xffffffff << 32;

不是由标准定义的。 (请参阅评论。)


您还应该收到编译器警告:

warning C4293: '<<' : shift count negative or too big, undefined behavior

You never initialized b to anything before you use it here:

b = 0xffffffff << b;

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:

a = 0xffffffff << 32;

is not defined by the standard. (See comments.)


You should also be getting a compiler warning:

warning C4293: '<<' : shift count negative or too big, undefined behavior
早乙女 2025-01-08 08:48:55

在 C 中,左移超过提升操作数的宽度是未定义的行为。

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

我假设在下面的示例中 intunsigned int 类型是 32 位。

无后缀的十六进制整数常量的类型是相应列表中可以表示其值的第一个:intunsigned intlong无符号长整型长整型无符号长整型

所以这里 0xFFFFFFFF 的类型是 unsigned int 并且 0xFFFFFFFF << 32 是未定义的行为。

In C it is undefined behavior to left shift more than the width of the promoted operand.

(C99, 6.5.7p3) "If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined."

I assume in the examples below int and unsigned 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 type unsigned int and 0xFFFFFFFF << 32 is undefined behavior.

手心的温暖 2025-01-08 08:48:55

没有人提到这个问题的另一个有趣的方面。在 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.

无悔心 2025-01-08 08:48:55

我敢打赌,在修正你的拼写错误后,你会得到 2 个零。原因如下:
如果您的程序(我打赌是)编译为版本,则您已打开优化。这意味着编译器将使用所谓的“常量折叠”,而 CPU 甚至不会执行这些移位。在底层代码中,会将 2 个零常量压入堆栈并调用 printf。这些操作(移位)的结果本质上将成为程序中的常量。所以不存在未定义的行为等 - 你的 a 和 b 变成常量值,你会得到类似的结果:

push 0
push 0
push offset to printf format string
call to printf

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:

push 0
push 0
push offset to printf format string
call to printf
把时间冻结 2025-01-08 08:48:55

你有 b = 0xffffffff << b;,但也许您的意思是 b = 0xffffffff << c;

You have b = 0xffffffff << b;, but perhaps you meant b = 0xffffffff << c;

所谓喜欢 2025-01-08 08:48:55

为什么它应该为零?您可以将其移动一个垃圾数(b 的第一个值),该数字可以为 0。

Why should it be zero? You shift it by a garbage number (the first value of b) that can be 0.

谈下烟灰 2025-01-08 08:48:55

您没有初始化 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.

泪之魂 2025-01-08 08:48:55

当您到达 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 instruction b = 0xffffffff << b;. Which expalins the given answer.

迷爱 2025-01-08 08:48:55

好的做法是轮班后 AND。在这种情况下,你清楚地知道会发生什么。

例如:

b << = 4;
b &= 0xfffffff0;

Good practice is AND after shift. In this case you know stritcly whats happen.

Examply:

b << = 4;
b &= 0xfffffff0;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文