我一直在用c编程。但从未使用过整数环绕的程序。我知道如果整数被分配4个字节,则整数范围变为-2,147,483,648到2,147,483,647。如果我们超过限制,它就会包裹起来。
我正在使用以下程序来了解环绕是如何发生的。
#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只允许环绕一次? 任何形式的帮助将不胜感激。感谢
答案 0 :(得分:2)
严格地说,有符号整数的溢出是undefined behavior。然而,在实践中,大多数实现使用2的补码表示来表示整数,并且回绕将按照您描述的方式工作。
考虑到这一点,让我们看看这里发生了什么。
随着循环的进行,最终s
的值将为1610612735.到目前为止,没有任何异常情况发生。现在我们乘以2并加一。此时结果溢出。让我们看看这些数字的十六进制表示。
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的左移一致。