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

use*_*774 19 c c++ 32bit-64bit

情况如下:

  1. 32位整数溢出
  2. malloc,期望 64位整数使用此整数作为输入

现在在64位机器上,哪个语句是正确的(如果有的话):

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

  1. Malloc将其读取为64位整数,发现11111111001101100000101011001000#################################是一个通配符位,表示在原始整数之后存储的任何数据.换句话说,它读取接近其最大值2 ^ 64的结果并尝试分配一些quintillion字节.它失败.
  2. Malloc将其读取为64位整数,转换为0000000000000000000000000000000011111111001101100000101011001000,可能是因为它是如何加载到寄存器中而使大量位为零.它不会失败,但会分配负内存,就像读取正无符号值一样.
  3. Malloc读取它为64位整数,转换为################################11111111001101100000101011001000,可能是因为它是如何加载到寄存器中的#一个表示寄存器中先前数据的通配符.根据最后一个值,它无法完全失败.
  4. 整数根本不会溢出,因为即使它是32位,它仍然是64位寄存器,因此malloc工作正常.

我实际测试了这个,导致malloc失败(这意味着1或3是正确的).我认为1是最合乎逻辑的答案.我也知道修复(使用size_t作为输入而不是int).

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

Mat*_* M. 18

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

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

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

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

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

  • @KyleStrand:在gcc中,类似`unsigned mulMod65535(unsigned short x,unsigned short y){return(x*y)&0xFFFF;}`如果`x*y`超过0x7FFFFFFF,可能在调用代码中产生奇怪的副作用,但gcc的作者并不认为这是一个问题.C标准的作者在已发布的Rationale中描述了他们如何期望大多数当前实现在产生INT_MAX + 1u和UINT_MAX之间的数值的整数表达式被强制为`unsigned`的情况下起作用,但是是否实际行为的问题是实施质量问题. (2认同)

das*_*ght 13

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

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

但是,根据C++标准,malloc参数的类型,即size_t始终是无符号的.当您将负数转换为无符号数时,它会进行符号扩展(请参阅此答案以进行讨论并参考标准),这意味着原始的最高位(1适用于所有负数)已设置在无符号结果的前32位中.

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