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

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

在此处输入图像描述

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

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

 400 * 400 = 160000 // Integer overflow wrap-around. 160000 % 2^16 = 28928 // Integer Division 28928 / 400 = 72 (rounded down) 

当然,对于较大的数据类型,这种溢出不会发生,因此您将返回400

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

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


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

  • 为什么带有GCC的x86上的整数溢出会导致无限循环?
  • 编译器优化导致程序运行速度变慢

看起来你猜对了。

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

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

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

更新#1

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

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

首先要知道的是,在C中, 整数溢出是未定义的行为。

(C99,6.5.5p5)“如果在评估表达式期间发生exception情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),则行为未定义。”

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

(C99,3.4.3p3)“示例未定义行为的示例是整数溢出的行为。”

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

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

你的声明是这样的:

 int i = 400 * 400 / 400; 

假设int在您的平台中是16位且签名表示是二进制补码,则400 * 400等于160000 ,其不能表示为intINT_MAX值为32767 。 我们存在整数溢出,实现可以做任何想做的事情

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

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

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

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