C++ 为什么int i=400*400/400给出结果72,数据类型是循环的?
我认为首先C++ 为什么int i=400*400/400给出结果72,数据类型是循环的?,c++,c,types,C++,C,Types,我认为首先400*400=160000被转换为28928,从0开始,以循环方式将160000时间转换为int类型(假设sizeof(int)=2字节),假设如下: 然后将28928除以400,其下限为72,结果因变量类型而异。我的假设正确吗?或者有其他解释吗?假设您使用的是一个非常旧的编译器,其中int只有16位。那么是的,你的分析是正确的* 400 * 400 = 160000 // Integer overflow wrap-around. 160000 % 2^16 = 28928
400*400=160000
被转换为28928,从0开始,以循环方式将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位整数示例不能保证有效。)
虽然很少见,但以下是一些有符号整数溢出未如预期那样结束的示例:
为了从下面的注释中得出完整的答案,您看到这种行为的原因是大多数主要编译器在这些情况下都会优化速度,并且在简单的算术运算之后不会添加安全检查。正如我上面所述,硬件根本没有空间来存储这些额外的位,这就是为什么会出现“循环”行为。首先要知道的是,在C中,整数溢出是未定义的行为 (C99,6.5.5p5)“如果在表达式求值期间出现异常情况(即,如果结果未在数学上定义或不在其类型的可表示值范围内),则行为未定义。” C说得很清楚,并在这里重复: (C99,3.4.3p3)“未定义行为的一个示例是整数溢出行为。” 请注意,整数溢出仅将有符号整数视为无符号整数而不会溢出: (C99,6.2.5p9)“涉及无符号操作数的计算永远不会溢出,因为不能由结果无符号整数类型表示的结果将被减少为比结果类型可以表示的最大值大一的数的模。” 您的声明如下:
int i = 400 * 400 / 400;
假设int
在您的平台中是16位的,并且有符号表示是2的补码,400*400
等于160000
,它不能表示为int
,int\u MAX
值为32767
。我们遇到了整数溢出,实现可以做它想做的任何事情
通常,在此特定示例中,编译器将执行以下两种解决方案之一:
400*400/400
的结果是72
400*400/400
减少到400
。这通常是由优秀的编译器在启用优化选项时完成的在
gcc
中,要始终包装溢出,可以启用-fno strict overflow
选项(默认情况下禁用)。例如,这是Linux内核的选择;他们使用此选项编译以避免不好的意外。但这也限制了编译器不能执行它可以执行的所有优化。在您的平台上int的大小是多少sizeof(int)
实际上int就是一个例子(假设它是2字节),我真正想知道的是,数据类型是否以循环方式工作,我的意思是wrapparound是否适用于数据类型。如果您对这些主题感兴趣,我建议您了解什么是,检查“无符号”数字与“2的补码”的二进制表示形式并画出关系图。然后您就会明白,所有无符号或2的补码运算都是全等的2^n
,其中n
是数据类型中的位数。这个同余就是你们所说的“循环”,我很久以前在一门离散数学课程中就知道了同余:)它不一定会像所描述的那样表现。C和C++中的符号整数溢出是未定义的行为。@ DealLaxx:NULL在实际规范中也被保留为“开放编译器解释”,但在所有编译器中,我知道它总是0。一般来说,你可能是对的,我只对HP-UX和Windows(VS和Intel编译器)有经验。这些都是这样的,我不能代表所有其他编译器。但OP的编译器的行为很可能与