在C 中,签名类型的溢出是未定义的行为。以下示例也是不确定的行为吗?
#include <limits.h>
int f() {
int a = INT_MAX;
int b = -1;
return a + b;
}
它不是数学上下文中的溢出,但是CPU可能会看到它 add 0x7fffffff 0xffffffff
。
您给出的示例不是溢出。
来自Wikipedia(https://en.wikipedia.org/wiki/integer_overflow):
...当算术操作试图尝试时,会发生整数溢出 创建一个超出范围的数字值 代表给定数量的位 - 要么大于 最大或低于最小代表值。
int_max (-1)不超过以int类型表示的范围,并定义了结果。
,因为INT_MAX + (-1)
的结果在int
的代表值范围内,它是正确定义的
您应该停止将语言视为汇编或机器代码上的薄层。
关于程序的不确定性,没有CPU,只有该程序运行的抽象计算机。
在两个组合系统上,随身携带和溢出是不同的概念。许多这样的系统旨在支持多字算术。如果将值解释为未签名的算术总和将超过类型的范围,则将报告随身携带。在任何计算中,将溢出到上部位与运行不同之后,将报告溢出,但在计算上字节后才有意义。
在8位系统上," int"通常是两个字节,int_max为0x7f:0xff:0xff和-1为0xff:ff。通过添加两个较低字节,然后添加两个上的字节,然后从下部添加两个上字节。
-
将0xff添加到0xff可产生0xfe并携带但没有溢出(均携带到上钻头)。
-
将0x7f添加到0xff的0x7e无携带或溢出(既不载入或从上部均出现)。
这样的细节通常对C程序员不重要,因为编译器通常没有提供访问溢出标志的方法,也没有任何方法来确保以某种方式执行计算,以使标志在任何特定点有意义代码。尽管如此,C89理由注释:
c代码可以是不可折叠的。
尽管它努力使程序员有机会编写真正的便携式程序,但C89委员会不想强迫程序员写作 便便的是,为了排除C用作"高级汇编器":编写机器的能力 - 特定代码是C的优势之一。这一原则在很大程度上激发了严格符合程序和符合程序之间的区别。(§4)。
注意,顺便说一句,制作小型无符号类型的理由促进了签名的int
,强烈暗示C89的作者期望某事像以下内容一样,在普通平台上应该是安全的:
unsigned mul_mod_65536(unsigned short x, unsigned short y)
{ return (x*y) & 0xFFFF; }
尽管如此,当通过过度"巧妙"优化GCC等编译器处理时,在常见的32位平台上处理时,此类代码可能会发生故障。没有理由为此功能生成的机器代码应该关心x*y
的值是否超过INT_MAX
,因为结果的上位将被切碎,并且在任何情况下,下部的较低位都是正确的。但是,在某些情况下,如果GCC知道,例如Y将为65535,因此可以决定,由于x*y
不能"超过INT_MAX,x
"不能超过0x8000。标准的作者可能不想排除C用作高级汇编程序的可能性,但这并不意味着编译器作家有这种感觉。