c - c 中整数的环绕

标签 c integer-overflow

我已经用 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。 The snapshot of gdb

然后它永远保持为-1。我的疑问是为什么值变为-1后没有发生回绕。我知道二进制中 s 的值将全为 1 或十六进制中的 FFFFFFFF 。而且它不会永远改变(它在内部更新,但我们只能看到最后 32 位,所以它是 -1)。但是这次环绕没有出现吗?它依赖于编译器吗?或者 gcc 只允许环绕一次? 任何形式的帮助将不胜感激。谢谢

最佳答案

严格来说,有符号整数的溢出是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 一致。

关于c - c 中整数的环绕,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45484643/

相关文章:

c - C11 中是否有一种机制来确保否定整数常量的预期类型?

java - Java long 和 double sum 的转换总是给出相同的结果

c - 从算术运算中获取溢出

c - 如何使生成的自动工具 ./configure 强制 C11 的 _Generic 的可用性?

c++ - 如何检测无符号整数溢出?

C编程sprinf问题

c++ - 从 haskell 调用 C opencv 函数

c++ - 进行大乘法运算时避免 int 溢出

c - C语言中的while循环

c - printf 在 malloc 之后打印一个奇怪的字符