【问题标题】:overflow with left shift左移溢出
【发布时间】:2015-10-01 22:53:32
【问题描述】:

我有一个简单的程序,其中有一些用于位操作的宏。

其中一个宏转换为跟随

unsigned long val = 1 << 0x1f;

在这里,我得到的输出是 `

val = 0xffffffff80000000;

我知道这是某种 int 溢出。我对最终结果的部分感到困惑。为什么结果是我得到的结果?

(出于某种原因,我确实在这里理解 1 被视为 int 并且当左移使其成为负 int 时。但是我对类型转换感到困惑的部分。)

【问题讨论】:

  • 你可能想用1L &lt;&lt; 0x1f把它变成一个长的。
  • 是的,这行得通。当赋值为 long 时,编译器无法理解它,这有点难过。
  • @Jester:这仍然是未定义的行为,因为转移到了符号位。实际上,它必须是一个无符号整数:1LU.
  • @Olaf 他显然在使用long 是64 位的系统,因为31 的移位不是符号位,所以它不是未定义的。但是,是的,因为预期的类型是unsigned long,所以最好使用UL
  • @Jester:抱歉,这是指 OPs 版本。但是,使用无符号是可移植的方式。永远不要依赖基本类型的宽度(如果需要固定宽度类型,请使用 stdint.h 类型)。

标签: c


【解决方案1】:

1 是一个 有符号 整数,所以在溢出的情况下它是未定义的行为。

根据标准(强调我的):

E1 &lt;&lt; E2 的结果是E1 左移E2 位位置;空出的位被填充 零。如果E1 有无符号类型,则结果的值为E1 × 2E2,减少模 比结果类型中可表示的最大值大一。如果E1 有一个签名 类型和非负值,并且 E1 × 2E2 可以在结果类型中表示,那么就是 结果值; 否则,行为未定义

改为尝试unsigned long1ul

unsigned long val = 1ul << 0x1f

(出于某种原因,我确实理解这里 1 被视为 int 并且当左移使其成为负整数时。但是我对类型转换感到困惑的部分。)

如果符号位设置为 1,实际上可能会发生这种情况。(但是,再一次,根据标准,它是 UB。)

不过,让我们考虑一个法律案例(我使用了shortint,因为intlong 在我的系统上具有相同的大小):

                        // on my system
short int s = SHRT_MIN; // 0x8000
unsigned int i = s;     // 0xffff8000

标准的以下部分对此进行了澄清:

6.3.1.3 有符号和无符号整数

当整数类型的值被转换为_Bool以外的其他整数类型时,如果 该值可以用新类型表示,它是不变的。

否则,如果新类型是无符号的,则通过重复添加或转换值 比新类型可以表示的最大值减一 直到值在新类型的范围内

(如果我理解正确的话)因为我们不能以无符号类型表示负值,所以我们将 UINT_MAX + 1 添加到 SHRT_MIN(纯数学术语,不考虑溢出):

UINT_MAX + 1 + SHRT_MIN // 0xffff8000

【讨论】:

  • 所以,我的情况是未定义的行为。
  • @agent.smith:你应该庆幸没有得到鼻部守护进程。
  • @agent.smith 刚刚更新了一些关于积分转换的词。
  • 谢谢大家。事实证明,这对我来说是痛苦的。这是一个巨大的代码库的一部分,并且很少失败,因为它是未定义的行为,并且在我的代码库中左移超过 30 的情况很少发生。我不得不出汗 2 天来解决这个错误.
猜你喜欢
  • 2011-04-21
  • 2019-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多