C 为什么长整型的负数会产生溢出?

C 为什么长整型的负数会产生溢出?,c,long-integer,integer-overflow,C,Long Integer,Integer Overflow,当我编写以下内容时,它不会给出编译警告/错误 long universe_of_defects = 4294967295L; 但是当我否定long int时,它会给出编译警告 long universe_of_defects = -4294967295L; warning: overflow in conversion from 'long long int' to 'long int' changes value from '-4294967295' to '1' [-Wover

当我编写以下内容时,它不会给出编译警告/错误

  long universe_of_defects = 4294967295L;
但是当我否定long int时,它会给出编译警告

  long universe_of_defects = -4294967295L;

 warning: overflow in conversion from 'long long int' to 'long int' changes value from '-4294967295' to '1' [-Woverflow]
为什么?

另外,下面是long int的最小值,编译时不会发出警告。 长宇宙的缺陷=2147483648L


根据文档,32位可以保存0到4294967295(如果无符号)或-2147483648到+2147483647(如果有符号)。但它看起来像是持有-2147483648到4294967295。怎么可能呢?

谢谢大家的见解。这就是我想出来的

长变量保存从-2147483648到+2147483647的值。所以它使用32位(4字节)。语句“long universe\u of_defects=4294967295L;”在编译期间不显示任何警告消息,并存储二进制值,如下所示

1111111111111111111111111111111111111111111111111

在二的补语中,它被解释为-1。因此,以下代码的输出是:

long universe_of_defects = 4294967295L;
printf("The entire universe has %ld bugs.\n",universe_of_defects);


The entire universe has -1 bugs.

谢谢大家的见解。这就是我想出来的

长变量保存从-2147483648到+2147483647的值。所以它使用32位(4字节)。语句“long universe\u of_defects=4294967295L;”在编译期间不显示任何警告消息,并存储二进制值,如下所示

1111111111111111111111111111111111111111111111111

在二的补语中,它被解释为-1。因此,以下代码的输出是:

long universe_of_defects = 4294967295L;
printf("The entire universe has %ld bugs.\n",universe_of_defects);


The entire universe has -1 bugs.

4294967295L
表示为64位整数2的二进制补码
0000_0000_0000_0000_0000_1111_1111_1111_1111
,但作为32位整数没有表示(32位整数从
-2147483648
2147483647
)因此,转换为32位有符号整数将始终导致实现定义的行为。64位有符号二的补码中的值
-4294967295L
1111_1111_1111_1111_0000_0000_0001
,这可能是将其“转换”为32位的原因(通过截断32个最有意义的位),最终值会产生一个正数
+1
(截断后会产生模式
0000_0000_0000_0001
+1
),这可以解释从编译器得到的警告消息(在本例中,您在转换过程中丢失了符号位,但如果编译器将第63位移到位置31,结果将是
1000_0000_0000_0001
,这是
-2147483647
,与您在源代码中使用的原始值无关)


您必须编写可移植代码,并使用可在所用类型之间转换的值。编译器只是在告诉您转换过程中发生了什么。

4294967295L
表示为64位int-two的补码,即二进制
0000_0000_0000_1111_1111_1111_1111
,但表示为32位整数没有表示(32位整数从
-2147483648
2147483647
)因此,转换为32位有符号整数将始终导致实现定义的行为。64位有符号二的补码中的值
-4294967295L
1111_1111_1111_1111_0000_0000_0001
,这可能是将其“转换”为32位的原因(通过截断32个最有意义的位),最终值会产生一个正数
+1
(截断后会产生模式
0000_0000_0000_0001
+1
),这可以解释从编译器得到的警告消息(在本例中,您在转换过程中丢失了符号位,但如果编译器将第63位移到位置31,结果将是
1000_0000_0000_0001
,这是
-2147483647
,与您在源代码中使用的原始值无关)


您必须编写可移植代码,并使用可在您使用的类型之间转换的值。编译器只是在告诉您转换过程中发生了什么。您的代码在这两种情况下都具有实现定义的行为,因为表达式在32位系统上具有type
long-long int
,并且超过了e类型的范围

C标准明确了这一点:

6.3.1.3有符号和无符号整数

  • 当整数类型的值转换为除
    \u Bool
    以外的其他整数类型时,如果该值可以用新类型表示,则该值不变
  • 否则,如果新类型是无符号的,则会通过重复地将新类型中可以表示的最大值加上或减去一个值来转换该值,直到该值在新类型的范围内
  • 否则,将对新类型进行签名,并且无法在其中表示值;要么结果是实现定义的,要么引发实现定义的信号
  • 混淆可能来自于
    L
    后缀,该后缀不强制常量的类型,但使其至少与
    long int
    一样大。如果编译器将标记此类混淆的副作用,则会有所帮助,但C标准不强制要求进行此类诊断

    下面是一个程序来说明这一点:

    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>
    
    #define typeof(X)  _Generic((X),                                        \
                       long double: "long double",                          \
                       double: "double",                                    \
                       float: "float",                                      \
                       unsigned long long int: "unsigned long long int",    \
                       long long int: "long long int",                      \
                       unsigned long int: "unsigned long int",              \
                       long int: "long int",                                \
                       unsigned int: "unsigned int",                        \
                       int: "int",                                          \
                       unsigned short: "unsigned short",                    \
                       short: "short",                                      \
                       unsigned char: "unsigned char",                      \
                       signed char: "signed char",                          \
                       char: "char",                                        \
                       bool: "bool",                                        \
                       default: "other")
    
    int main() {
    #define TEST(x)  printf("%8s has type %s and size %zu\n", #x, typeof(x), sizeof(x))
        TEST(4294967295);
        TEST(4294967295U);
        TEST(4294967295L);
        TEST(4294967295UL);
        TEST(4294967295LL);
        TEST(4294967295ULL);
        TEST(0xffffffff);
        TEST(0xffffffffU);
        TEST(0xffffffffL);
        TEST(0xffffffffUL);
        TEST(0xffffffffLL);
        TEST(0xffffffffULL);
        TEST(2147483647);
        TEST(2147483648);
        TEST(2147483647+1);
        TEST(-2147483647);
        TEST(-2147483648);
        TEST(-2147483647-1);
    
        return 0;
    }
    

    在这两种情况下,代码都具有实现定义的行为,因为表达式在32位系统上具有类型
    long-long-int
    ,并且超出了类型
    long
    的范围

    C标准明确了这一点:

    6.3.1.3有符号和无符号整数

  • 当整数类型的值转换为除
    \u Bool
    以外的其他整数类型时,如果该值可以用新类型表示,则该值不变
  • 否则,如果新类型是无符号的,则通过重复地加上或减去一个值来转换该值