在C编程语言中,有符号整数转换为更小的有符号整数(例如 char 类型)时,其行为是由实现定义的。尽管许多系统和程序员假设可以进行环绕溢出,但这不是一种标准行为。那么什么是环绕溢出呢?
在二进制补码表示法中,环绕溢出会发生在一个值无法用当前类型来表示时,它会在最高或最低可表示数字之间循环。这是什么意思?看看下面的例子。
在 signed char 中,可以表示的最大值为 127,最小值为 -128。当我们执行 "char i = 128" 时,存储在 i 中的值变成了 -128。因为该值比有符号整数类型要大,所以它绕过了最小值,并且如果是 "char i = 129",则 i 将包含 -127。你看到了吗?每当一个端点达到其最大值时,它就会绕到另一端(符号)。反之亦然,如果 "char i = -129",则 i 将包含 127,如果是 "char i = -130",则将包含 126,因为它达到了最大值并绕过了最高值。
(最高)127、126、125、...、-126、-127、-128(最低)
如果值非常大,它将一直绕过,直到达到其范围内可以表示的值。
更新:与 char 和 short 不同,“int” 无法起作用的原因是当两个数字相加时,存在溢出的可能性(不管是 int、short 还是 char,同时不要忘记整体提升),但因为“short”和“char”的大小比“int”小,并且因为它们在表达式中被提升为“int”,所以它们会再次表示而不截断。
return (sum-x==y) && (sum-y==x);
任何溢出都会被检测到,如下面详细解释,但是当使用int时,它不会提升为任何类型,因此会发生溢出。例如,如果我执行INT_MAX+1,则结果为INT_MIN,如果我通过INT_MIN-1 == INT_MAX测试溢出,则结果为TRUE!这是因为"short"和char会被提升为int进行评估,然后被截断(溢出)。但是,int首先会发生溢出,然后再进行评估,因为它们没有被提升为更大的大小。
想象一下没有提升的char类型,并尝试使用上面的说明制造溢出并检查它们。您会发现添加或减去导致溢出的值会将您带回原来的位置。然而,在C中并非如此,因为char和"short"被提升为int,因此检测到了溢出,在int中则不是这样,因为它没有被提升为更大的大小。
更新结束
对于您的问题,我在MinGW和Ubuntu 12.04中检查了您的代码,似乎正常工作。后来我发现,该代码实际上适用于短小于int的系统,并且值未超过int范围。该行:
return (sum-x==y) && (sum-y==x);
是正确的,因为"sum-x"和"y"被评估为(int),所以在这里没有发生上溢,而在前一行(赋值时)发生了:
short sum = x+y;
这里是一个测试。如果我在第一个输入32767,在第二个输入2,那么当:
short sum = x+y;
由于数据溢出,变量sum将包含-32767。但是当:
return (sum-x==y) && (sum-y==x);
"sum-x"(-32767 - 32767)只有在发生环绕时才会等于y(2)(然后出现错误),但由于整数提升,它从未以这种方式发生,因此"sum-x"的值变为-65534,而不等于y,这导致正确的检测。
这是我使用的代码:
#include <stdio.h>
short add_ok( short x, short y ){
short sum = x+y;
return (sum-x==y) && (sum-y==x);
}
int main(void) {
short i, ii;
scanf("%hd %hd", &i, &ii);
getchar();
printf("%hd", add_ok(i, ii));
return 0;
}
请查看这里和这里。
您需要提供您正在使用的架构以及测试的实验值,因为并非每个人都会遇到您所说的问题,并且由于问题的实现定义性质。
参考资料:C99 6.3.1.3 这里,GNU C手册这里。
SHRT_MAX - x <= y
或者类似的东西。 - Kerrek SBint
,您可以将两个参数都设置为INT_MAX
进行检查。 对于char
,正如 @R.. 所说,它不会溢出。 - Rsh