在c中包含整数的部分
我一直在用c编程一段时间。但从来没有使用整数环绕的程序。我知道如果整数分配4个字节,那么整数范围变为-2,147,483,648至2,147,483,647。如果我们超过了限制,它只是环绕一下。在c中包含整数的部分
我正在使用下面的程序来了解如何环绕发生。
#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是否只允许环绕一次? 任何形式的帮助,将不胜感激。谢谢
严格来说,有符号整数的溢出是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一致。
“大多数实现使用2的补码表示整数和环绕将工作如何描述。”意味着2的补充机器**将环绕。尽管这是常见的行为,但即使是带补码机器的兼容编译器也可能会在各种情况下环绕,因为它是未定义的行为。那些讨厌的新C编译器利用该UB优化代码。 – chux
简短回答:适当的环绕保证*只*为无符号类型。使用签名类型,可能会发生奇怪的事情,因为它在技术上是不确定的。 –
它不再更新,因为'2 *( - 1)+ 1 = -1'。 –