为什么';t左位移位,“<<&引用;,对于32位整数,当使用超过32次时是否能按预期工作? 当我编写以下程序并使用GNU C++编译器时,输出是 1 ,这是由编译器执行的旋转操作引起的。 #include <iostream> int main() { int a = 1; std::cout << (a << 32) << std::endl; return 0; } #包括 int main() { INTA=1; STD::CUT< P>根据C++标准:未定义行为:< /P>

为什么';t左位移位,“<<&引用;,对于32位整数,当使用超过32次时是否能按预期工作? 当我编写以下程序并使用GNU C++编译器时,输出是 1 ,这是由编译器执行的旋转操作引起的。 #include <iostream> int main() { int a = 1; std::cout << (a << 32) << std::endl; return 0; } #包括 int main() { INTA=1; STD::CUT< P>根据C++标准:未定义行为:< /P>,c++,bit-shift,C++,Bit Shift,E1的值将32位变量移位32位或更多位是未定义的行为,可能会导致编译器使守护进程飞出您的鼻子 说真的,大多数时候输出都是0(如果int是32位或更少),因为您正在移动1,直到它再次下降,只剩下0。但是编译器可能会优化它,以执行它喜欢的任何操作 参见优秀的LLVM博客条目,每个C开发人员都必须阅读。在C++中,< P>,如果你移动的值比类型的大小少,则只有SHIFT定义好。如果 int >代码32位,那么只有0个,包括31个步骤是明确定义的。 那么,这是为什么 如果您查看执行移位的底层硬件,如果它

E1的值将32位变量移位32位或更多位是未定义的行为,可能会导致编译器使守护进程飞出您的鼻子

说真的,大多数时候输出都是0(如果
int
是32位或更少),因为您正在移动1,直到它再次下降,只剩下0。但是编译器可能会优化它,以执行它喜欢的任何操作


<>参见优秀的LLVM博客条目,每个C开发人员都必须阅读。在C++中,

< P>,如果你移动的值比类型的大小少,则只有SHIFT定义好。如果<代码> int >代码32位,那么只有0个,包括31个步骤是明确定义的。 那么,这是为什么

如果您查看执行移位的底层硬件,如果它只需要查看值的低五位(在32位的情况下),那么它可以使用比必须检查值的每一位更少的逻辑门来实现

对评论中问题的回答


C和C++被设计成在任何可用的硬件上尽可能快地运行。今天,生成的代码只是一个“移位”指令,不管底层硬件如何处理指定范围之外的值。如果语言指定了移位应该如何操作,生成的代码可能需要检查移位计数。在执行移位之前在范围内。通常,这将产生三条指令(比较、分支、移位)。(无可否认,在这种情况下,由于移位计数已知,因此不需要这样做。)

由于您正在将一个int进行32位的位移位;您将得到:
警告C4293:“它无法按预期工作,因为您的期望太高了

在x86的情况下,硬件不关心计数器大于寄存器大小的移位操作(有关解释,请参见上的SHL指令说明示例)

<> P> C++标准不想在这些情况下告诉我们要做什么,因为生成的代码必须为每个参数移位添加额外的检查和逻辑。 有了这种自由,编译器的实现者可以只生成一条汇编指令,而无需任何测试或分支

例如,更“有用”和“逻辑”的方法是使用
(x>-y)
,以及以逻辑和一致的行为处理高位计数器

然而,这将需要更慢的位移位处理,因此选择做硬件所做的事情,让程序员需要为副例编写自己的函数


在这些情况下,不同的硬件做了不同的事情,标准所说的是“无论你做什么奇怪的事情,只要不怪C++,这是你的错”,在法律术语中翻译。< /P> < P> LyydDigor的答案,6502解释为什么(在一些机器上)打印的代码是<代码> 1 < /代码>。(尽管操作的行为尚未定义)。我正在添加细节,以防它们不明显

我假设(和我一样)您正在英特尔处理器上运行该程序。GCC为移位操作生成以下汇编指令:

movl $32, %ecx
sall %cl, %eax
关于
sall
和其他轮班操作的主题,手册第624页说:

8086不屏蔽移位计数。但是,所有其他英特尔体系结构处理器 (从Intel 286处理器开始)将移位计数屏蔽为5位,导致 最大计数为31。此掩蔽在所有操作模式下完成(包括虚拟8086 模式)以减少指令的最大执行时间


由于32的下5位为零,因此
1您可以尝试以下操作。这实际上会在
32
左移位后将输出设置为
0

#include<iostream>
#include<cstdio>

using namespace std;

int main()
{
  int a = 1;
  a <<= 31;
  cout << (a <<= 1);
  return 0;
}
#包括
#包括
使用名称空间std;
int main()
{
INTA=1;

a这是由于C中未定义的行为和为IA-32处理器生成的代码在移位计数上应用了5位掩码这一事实的组合造成的。这意味着在IA-32处理器上,移位计数的范围仅为0-31

来自C编程语言2

如果右操作数为负,或大于或等于左表达式类型中的位数,则结果未定义

摘自IA-32英特尔体系结构软件开发人员手册3

8086不会屏蔽移位计数。但是,所有其他IA-32处理器(从Intel 286处理器开始)都会将移位计数屏蔽为5位,导致最大计数为31位。此屏蔽在所有操作模式(包括虚拟8086模式)下都会执行,以减少指令的最大执行时间



一,

2 A7.8换档操作员,附录A.参考手册,C编程语言


3 SAL/SAR/SHL/SHR–Shift,第4章。指令集参考,IA-32《英特尔体系结构软件开发人员手册》

我也遇到了同样的问题,这对我很有效:


f=((long-long)1您可能想确切地显示您使用了什么命令行(开关),什么版本的GCC,以及在哪个平台上。@xanatos您怎么知道这是一个“错误”的问题?!@awoodland我也这么认为。根据最新草案:“如果右操作数为负数,或大于或等于提升后的左操作数的位长度,则行为是未定义的。”(5.8)N3291问题是,正如OP所给出的,这个问题是不合逻辑的。他说:“看这里,它给了我
#include<iostream>
#include<cstdio>

using namespace std;

int main()
{
  int a = 1;
  a <<= 31;
  cout << (a <<= 1);
  return 0;
}