当64位机器上的32位整数溢出时会发生什么?

时间:2014-03-28 13:52:06

标签: c++ c 32bit-64bit

情况如下:

  1. 32位整数溢出
  2. malloc,期待 64位整数使用此整数作为输入
  3. 现在在64位机器上,哪个陈述是正确的(如果有的话)

    假设由于溢出,带符号的二进制整数11111111001101100000101011001000只是负数。这是一个实际存在的问题,因为您可能希望分配比在32位整数中描述的更多的字节。但它会以64位整数读入。

    1. Malloc将其读取为64位整数,查找11111111001101100000101011001000################################,其中#是表示原始整数之后存储的数据的通配符。换句话说,它读取接近其最大值2 ^ 64的结果并尝试分配一些quintillion字节。它失败了。
    2. Malloc将此读取为64位整数,转换为0000000000000000000000000000000011111111001101100000101011001000,可能是因为它是如何将其加载到寄存器中而使大量位为零。它不会失败,但会分配负内存,就像读取正无符号值一样。
    3. Malloc将其读取为64位整数,转换为################################11111111001101100000101011001000,可能是因为它是如何将其加载到寄存器中的#一个表示寄存器中先前数据的通配符。根据最后的值,它无法完全失败。
    4. 整数根本不会溢出,因为即使它是32位,它仍然是64位寄存器,因此malloc工作正常。
    5. 我实际测试了这个,导致malloc失败(这意味着1或3是正确的)。我认为1是最合乎逻辑的答案。我也知道修复(使用size_t作为输入而不是int)。

      我真的很想知道究竟发生了什么。出于某种原因,我没有找到任何关于如何在64位机器上实际处理32位整数以进行这种意外“演员”的澄清。我甚至不确定它在寄存器中是否真的很重要。

3 个答案:

答案 0 :(得分:18)

你的推理问题在于它假设整数溢出会导致确定性和可预测的操作。

不幸的是,情况并非如此:未定义的行为意味着任何事情都可能发生,特别是编译器可能会优化,好像它永远不会发生

因此,如果存在可能的溢出,几乎不可能预测编译器将生成什么类型​​的程序。

  • 可能的输出是编译器省略了分配,因为它不会发生
  • 可能的输出是结果值是0扩展或符号扩展(取决于它是否已知为正数)并被解释为无符号整数。您可以从0size_t(-1)获得任何内容,因此可能会分配太少或太多的内存,甚至无法分配,...
  • ...

未定义的行为=>所有投注均已关闭

答案 1 :(得分:13)

一旦整数溢出,使用其值会导致未定义的行为。根据标准,在溢出后使用int的结果的程序无效 - 基本上,关于其行为的所有赌注均已关闭。

考虑到这一点,让我们看一下在负数以二进制补码表示存储的计算机上会发生什么。在这样的计算机上添加两个大的32位整数时,如果发生溢出,则会得到否定的结果。

但是,根据C ++标准,malloc的参数类型,即size_t, is always unsigned。当您将负数转换为无符号数时,它会进行符号扩展(see this answer for a discussion and a reference to the standard),这意味着原始的最高位(所有负数为1)都设置在无符号结果的前32位。

因此,您获得的是第三种情况的修改版本,但不包括"通配符位#"它有一直到顶部。结果是一个巨大的无符号数(大约16 exbibytes左右);自然malloc无法分配那么多内存。

答案 2 :(得分:3)

因此,如果我们有一个特定的代码示例,一个特定的编译器和平台,我们可以确定编译器正在做什么。这是Deep C采用的方法,但即便如此,它可能无法完全预测,这是未定义行为的标志,对未定义行为进行概括并不是一个好主意。

我们只需要查看gcc文档中的建议,看看它有多乱。该文档提供了一些关于integer overflow的好建议,其中说:

  

实际上,许多可移植的C程序都假设有符号整数溢出使用二进制补码算法可靠地包装。然而,C标准表明程序行为在溢出时是不确定的,在少数情况下,C程序不适用于某些现代实现,因为它们的溢出并没有像作者所期望的那样包围。

并在 Practical Advice for Signed Overflow Issues小节中说:

  

理想情况下,最安全的方法是完全避免有符号整数溢出。[...]

在一天结束时,它是未定义的行为,因此在一般情况下是不可预测的,但在gcc的情况下,在Integer的实现定义部分中说整数溢出包围:< / p>

  

为了转换为宽度为N的类型,将该值减去模2 ^ N以在该类型的范围内;没有信号被提出。

但是在他们关于整数溢出的建议中,他们解释了optimization can cause problems with wraparound

的方式
  

编译器有时会生成与环绕整数运算不兼容的代码。

所以这很快就变得复杂了。