【问题标题】:1 << 31 produces the error, "The result of the '<<' expression is undefined"1 << 31 产生错误,“'<<' 表达式的结果未定义”
【发布时间】:2014-10-11 21:47:26
【问题描述】:

如果我在 32 位平台上将值声明为 int 类型并执行以下操作:

int32_t mask;
mask = 1 << 31: // produces 2147483648 (or 0x80000000)

有人能帮我理解为什么上面的行会产生警告吗:

The result of the '<<' expression is undefined

【问题讨论】:

  • 哪个编译器,哪个语言,哪个版本的标准?这是在 C++14 中实现定义的,在所有其他版本的 C 和 C++ 中未定义。
  • @jww 怎么是重复的?得分最高的答案明确指出“未定义 32 或更多的班次”。这大约是 31 次。在这种情况下,答案是 int32_tsigned long int 的 typedef 这一事实使它成为一个相同的问题,因为它已签名。
  • 尝试以下操作来消除警告:mask = (int32_t)(1UL &lt;&lt; 31);
  • @Niels - 阅读问题和答案。这种转变导致这个问题和引用的重复项都发生了符号变化。但你是对的......引用的问题应该说“超过31......”。

标签: c++ c


【解决方案1】:

231 在从 -231 到 (231-1) 的 int32_t 中无法表示。这是 C11 和 C++11 中未定义的行为,在 C++14 中是实现定义的。

C11 §6.5.7/p4(引用 N1570):

E1 &lt;&lt; E2 的结果是E1 左移E2 位位置; 空出的位用零填充。 [...] 如果E1 具有签名类型 和非负值,E1 × 2E2 可以表示为 结果类型,那就是结果值;否则, 行为未定义。

N3337 §5.8 [expr.shift]/p2 中的 C++11 规则几乎相同。由于 231 不可表示,因此行为未定义。

C++14 §5.8 [expr.shift]/p2(引用 N3936;另见 CWG issue 1457):

E1 &lt;&lt; E2的值是E1左移E2位位置; 空出的位用零填充。 [...] 否则,如果 E1 有一个签名 类型和非负值,E1×2E2是可表示的 在结果类型的相应无符号类型中,则 value,转换为结果类型,就是结果值; 否则,行为未定义。

由于 231 可以用无符号 32 位 int 表示,因此定义了行为并将结果 231 转换为 int32_t;此转换是根据 §4.7 [conv.integral]/p3 实现定义的。在使用二进制补码的典型系统中,您会得到 -231

【讨论】:

  • 这必须是未定义/实现定义的一个原因是,当计算为带符号的 32 位 int 时,一个补码和二进制补码硬件将为表达式 1 en.wikipedia.org/wiki/Ones%27_complement
  • 所有整数溢出实现都是在 C++14 中定义的,还是只是这种情况?
  • int32_t 是精确宽度类型。 C11 7.20.1.1 1 “typedef 名称 intN_t 指定宽度为 N、无填充位和二进制补码表示的有符号整数类型。...”。所以我仍然坚持int32_t 有一个固定的范围。如果系统不支持“宽度 N、无填充位和二进制补码表示”,则不支持int32_t
  • @chux 实际上,即使对于 2 的补码,C 标准也允许符号位 1 和全值位零作为陷阱表示 (6.2.6.2/2)。所以我相信拥有一个具有二进制补码表示但仍然具有范围 (-2^31 + 1) - (2^31 - 1) 的 int32_t 是合法的,通常是 -2^31一个陷阱表示。
  • @chux 很有趣,所以他们暗中禁止 intN_t 使用陷阱表示。
猜你喜欢
  • 2021-07-03
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-16
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
相关资源
最近更新 更多