我用c编程已经有很长一段时间了。但从来没有在整数绕回的程序中工作过。我知道如果整数被分配了4个字节,那么整数的范围就变成-2,147,483,648到2,147,483,647。如果我们超过了极限,它就会卷起来。
我正在使用下面的程序来了解wrap是如何发生的。
#include <stdio.h>
int main() {
int n = 4, s = 2;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
for (int k = 0; k < n; ++k)
{
s = 2 * s + 1;
}
}
}
printf("%d\n", s);
return 0;
}
我使用gdb来找出变量s的取值,我发现当我们执行最里面的循环30次时,s的值变成了负值,即-1073741825。然后,对于下一次迭代,它变为2147483647,对于第32次迭代,它变为-1。
那么它将永远保持为-1。我的疑问是,为什么在值变为-1之后没有发生回绕。我知道以二进制表示的s的值将是全1或以十六进制表示的FFFFFFFF。它不会永远改变(在内部它正在更新,但我们只能看到最后32位,所以它是-1)。但是,这一次是不是没有出现在画面中?它依赖于编译器吗?还是说,gcc只允许绕一次?任何形式的帮助都将不胜感激。谢谢
发布于 2017-08-03 13:08:27
严格地说,有符号整数的溢出是undefined behavior。然而,在实践中,大多数实现对整数使用2补码表示,而回绕将按照您所描述的方式工作。
考虑到这一点,让我们看看这里会发生什么。
随着循环的进行,s
的值最终将为1610612735。到目前为止,没有发生任何异常情况。现在我们乘以2,然后加1。此时,结果溢出。让我们看一下这些数字的十六进制表示。
1610612735d = 0101 1111 1111 1111 1111 1111 1111 1111 b = 0x5FFFFFFF
0x5FFFFFFF * 2 = 0xBFFFFFFE
0xBFFFFFFE + 1 = 0xBFFFFFFF
0xBFFFFFFE = 1011 1111 1111 1111 1111 1111 1111 1111 b = -1073741825d
从二进制的角度来看,乘以2相当于左移1。此操作将一个值移入符号位,得到一个负值。
在下一次操作中,乘以2会再次溢出。这次乘法将0移位到以前有1的符号位,因此符号再次改变:
0xBFFFFFFF * 2 = 0x7FFFFFFE
0x7FFFFFFE + 1 = 0x7FFFFFFF
0x7FFFFFFF = 0111 1111 1111 1111 1111 1111 1111 1111 b = 2147483647
下一次迭代也会溢出:
0x7FFFFFFF * 2 = 0xFFFFFFFE
0x7FFFFFFE + 1 = 0xFFFFFFFF
0xFFFFFFFF = 1111 1111 1111 1111 1111 1111 1111 1111 b = -1
现在我们得到-1。从现在开始,没有溢出:
-1 * 2 = -2
-2 + 1 = -1
以下是十六进制中的相同内容:
0xFFFFFFFF * 2 = 0xFFFFFFFE
0xFFFFFFFE + 1 = 0xFFFFFFFF
正如你所看到的,将-1加倍再加上1会再次得到-1,这就是为什么它不断重复的原因。这也与乘以2是1的左移是一致的。
https://stackoverflow.com/questions/45484643
复制