为什么int i = 400*400/400给出结果72,是数据类型循环?

Imr*_*ana 26 c c++ types

我认为首先从0开始400*400=160000转换为28928并以循环方式转换为160000时间为int类型(比如sizeof(int)= 2字节),假设它如下:

在此输入图像描述

然后将28928除以400,其中得到72,结果随变量的类型而变化.我的假设是正确的还是有其他解释?

Mys*_*ial 42

假设您使用的是一个可怕的老式编译器,其中int只有16位.那么是的,你的分析是正确的.*

400 * 400 = 160000 

//  Integer overflow wrap-around.
160000 % 2^16 = 28928

//  Integer Division
28928 / 400 = 72 (rounded down)
Run Code Online (Sandbox Code Playgroud)

当然,对于较大的数据类型,这种溢出不会发生,所以你会回来400.

*仅对无符号整数类型保证此环绕行为.对于有符号整数,它在C和C++中是技术上未定义的行为.

在许多情况下,有符号整数仍将表现出相同的环绕行为.但你不能指望它.(因此,保证带有带符号的16位整数的示例保持不变.)


虽然很少见,但这里有一些示例,其中有符号整数溢出未按预期包装:

  • 不仅"可怕的旧"编译器,而且现代微控制器架构的大量C实现都有一个16位的"int". (4认同)
  • 对,那是正确的.但请注意,只有无符号整数类型才能保证具有环绕行为.有符号整数类型在溢出时是未定义的行为(尽管在大多数情况下,它们仍然表现出相同的环绕行为). (2认同)

DXM*_*DXM 6

看起来你猜对了.

如果int是16位类型,那么它的行为与您描述的完全相同.您的操作按顺序进行,400*400产生160000,即10 0111 0001 0000 0000

当你将它存储在16位寄存器中时,顶部"10"将被切断,你最终得到0111 0001 0000 0000(28,928)....你猜对了.

你在哪个编译器/平台上构建它?典型的桌面至少是32位,所以你不会看到这个问题.

更新#1

注意:这是用您的特定编译器解释您的行为的原因.正如许多其他人很快指出的那样,不要假设所有编译器都采用这种方式.但你的具体肯定是.

为了完成下面评论的答案,您看到这种行为的原因是大多数主要编译器在这些情况下优化速度,并且在简单算术运算后不添加安全检查.因此,如上所述,硬件根本没有空间存储这些额外的位,这就是您看到"循环"行为的原因.

  • 它不一定表现如上所述.C和C++中的有符号整数溢出是未定义的行为. (2认同)
  • @DXM:当我们说签名溢出导致"未定义的行为"时,该术语在C标准中有一个非常具体的技术定义.一些实现溢出,一些实现终止程序,并且它们被允许让恶魔飞出你的鼻子.众所周知的编译器会优化您的程序,假设一个int永远不会溢出,导致写得不好的循环永远不会终止(例如). (2认同)
  • @ dan04:在实践中他们*做*.有些情况下,优化会改变有符号整数溢出的行为,因为"合法"允许优化器假定有符号整数类型不会溢出.有关详细说明,请参见[此处](http://www.airs.com/blog/archives/120). (2认同)

oua*_*uah 5

您必须要知道的第一件事是,在C中,整数溢出是未定义的行为.

(C99,6.5.5p5)"如果在评估表达式期间发生异常情况(即,如果结果未在数学上定义或在其类型的可表示值范围内没有),则行为未定义."

C说得非常清楚并在此重复:

(C99,3.4.3p3)"示例未定义行为的示例是整数溢出的行为."

请注意,整数溢出仅将有符号整数视为无符号整数永不溢出:

(C99,6.2.5p9)"涉及无符号操作数的计算永远不会溢出,因为无法用结果无符号整数类型表示的结果是以一个大于可由结果表示的最大值的数量的模数减少的.类型."

你的声明是这样的:

int i = 400 * 400 / 400;
Run Code Online (Sandbox Code Playgroud)

假设int在你的平台上的16位和符号表示为二进制补码, 400 * 400等于160000它不能表示为一个int,INT_MAX值是32767.我们存在整数溢出,实现可以做任何想做的事情.

通常,在此特定示例中,编译器将执行以下两种解决方案之一:

  1. 考虑溢出换行模数字大小,就像无符号整数那样,结果 400 * 400 / 40072.
  2. 利用未定义的行为将表达式减少400 * 400 / 400400.这通过优秀的编译器来完成,通常是在启用优化选项时.

请注意,整数溢出是未定义的行为,特定于C语言.在大多数语言(例如Java)中,它们以C语言中的无符号整数模数字大小.

gcc有溢出总是包,还有就是-fno-strict-overflow可以启用选项(默认禁用).这是例如Linux内核的选择; 他们使用此选项进行编译,以避免出现意外情况.但这也限制了编译器不执行它可以执行的所有优化.