带符号的长整数随着它的增长而变为负数?

时间:2017-04-14 14:13:50

标签: c

我正在做一些数学运算,然后打印一个有符号的长整数:

file1.c中

#include <stdio.h>

int main (int argc, char *argv[])
{
    long my_signed_integer = 9999L * 9999L * 9999L * 9999L;
    printf("This is my signed integer %ld.\n", my_signed_integer); 

    return 0;
}

生成文件

CFLAGS=-Wall -g

all: file1

clean:
    rm -f file1

我试图看看如果没有我的编译器给我一个错误,我会走多远,每次在乘法中再添加一个9999L,然后运行:

make
./file1.c

看看会发生什么。

4次

当使用9999L 4次时(如上例所示),我得到:

This is my signed integer 9996000599960001.

没有任何警告。

5次

使用9999L 5次,我收到1次警告:

warning: overflow in expression; result is 7716289630452291919 with type 'long'
      [-Winteger-overflow]

但文件仍在编译,最终结果是:

This is my signed integer 7716289630452291919.

6次

使用9999L 6次,我收到2个警告 - 一个带负数:

warning: overflow in expression; result is 7716289630452291919 with type 'long'
      [-Winteger-overflow]
        long my_signed_integer = 9999L * 9999L * 9999L * 9999L * 9999L * 9999L;
                                                               ^
warning: overflow in expression; result is -7550445434587511647 with type 'long'
      [-Winteger-overflow]
        long my_signed_integer = 9999L * 9999L * 9999L * 9999L * 9999L * 9999L;

唉,文件仍在编译,结果是:

This is my signed integer -7550445434587511647.

随着我添加越来越多的整数,这种模式仍在继续 - 我每次都会收到另一个警告。

首先,有人可以解释为什么编译器不会崩溃并拒绝编译文件?显然有一个溢出 - 为什么这是容忍的,而其他情况 - 例如乘以非常大的数字 - 会让它崩溃?

另外,为什么最终结果是负整数?

1 个答案:

答案 0 :(得分:6)

根据C标准,有符号整数类型的溢出为undefined behavior。因此编译器可以自由地生成它想要的任何行为。这也意味着可以自由生成警告。

实际上,在使用2的整数补码表示的托管实现上,溢出的行为就好像参数是无符号的(因此减少的模2 ^(位长))并且结果被解释为签名。

在您的特定情况下,long似乎是64位类型。因此,将9999L自身乘以4次适合该类型,但不再需要溢出。您获得的值是解释为signed long的结果的最低64位。

在5次的情况下,高阶位碰巧没有被设置,因此结果显示为正。在6次的情况下,高位被设置,因此它显示为负。