Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么int i=400*400/400给出结果72,数据类型是循环的?_C++_C_Types - Fatal编程技术网

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位整数示例不能保证有效。)


虽然很少见,但以下是一些有符号整数溢出未如预期那样结束的示例:


看来你猜对了

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

当您将其存储在16位寄存器中时,最前面的“10”将被截断,最终得到0111 0001 0000 0000(28928)。。。。其余的你都猜到了

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

更新#1

注意:这就是解释特定编译器行为的原因。正如许多其他人很快指出的,不要把这个答案当作所有编译器都是这样运行的。但你的特别的一个肯定会


为了从下面的注释中得出完整的答案,您看到这种行为的原因是大多数主要编译器在这些情况下都会优化速度,并且在简单的算术运算之后不会添加安全检查。正如我上面所述,硬件根本没有空间来存储这些额外的位,这就是为什么会出现“循环”行为。

首先要知道的是,在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
    。这通常是由优秀的编译器在启用优化选项时完成的
  • 请注意,整数溢出是未定义的,其行为特定于C语言。在大多数语言(例如Java)中,它们像C语言中的无符号整数一样,对单词大小进行模化


    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的编译器的行为很可能与