为什么 long int 的负值会溢出?
Why negative of a long int gives overflow?
当我写下面的时候,它没有编译warning/error。
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的最小值,编译时不报warning。
长 universe_of_defects = -2147483648L;
根据文档,32 位可以容纳从 0 到 4,294,967,295(如果无符号)或从 -2147483648 到 +2147483647(如果有符号)。但它看起来像持有-2147483648 到 4,294,967,295。怎么可能?
感谢大家的见解。这是我想出来的。
long 变量的值从 -2147483648 到 +2147483647。所以它使用 32 位(4 字节)。语句“long universe_of_defects = 4294967295L;”编译期间不显示任何警告消息,并存储二进制值如下。
1111 1111 1111 1111 1111 1111 1111 1111
在二进制补码中,解释为-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 位整数二进制补码 0000_0000_0000_0000_1111_1111_1111_1111
,但 32 位整数没有表示(32 位整数从 -2147483648
到 2147483647
) 因此转换为 32 位有符号整数将 始终导致 实现定义的行为 。 64 位有符号的值 -4294967295L
二进制补码是 1111_1111_1111_1111_0000_0000_0000_0001
,这可能是当 “转换” 到 32 位(通过截断 32 个最高有效位)时,最终值导致正数 +1
(截断导致模式 0000_0000_0000_0001
或 +1
),这可以解释您从编译器收到的警告消息(您在转换中丢失了符号位,在在这种情况下,但如果编译器将第 63 位移动到位置 31,结果将是 1000_0000_0000_0001
,即 -2147483647
的二进制补码,与您使用的原始值无关在源代码中)
您必须编写可移植代码,并使用可以在您使用的类型之间转换的值。编译器只是在告诉您转换过程中发生了什么。
您的代码在这两种情况下都具有实现定义的行为,因为表达式在您的 32 位系统上具有类型 long long int
并且超出了类型 long
.
的范围
C 标准对此很清楚:
6.3.1.3 Signed and unsigned integers
- When a value with integer type is converted to another integer type other than
_Bool
, if the value can be represented by the new type, it is unchanged.
- Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
- Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
混淆可能来自 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 位系统的输出(32 位 int
和 long
):
4294967295 has type long long int and size 8
4294967295U has type unsigned int and size 4
4294967295L has type long long int and size 8
4294967295UL has type unsigned long int and size 4
4294967295LL has type long long int and size 8
4294967295ULL has type unsigned long long int and size 8
0xffffffff has type unsigned int and size 4
0xffffffffU has type unsigned int and size 4
0xffffffffL has type unsigned long int and size 4
0xffffffffUL has type unsigned long int and size 4
0xffffffffLL has type long long int and size 8
0xffffffffULL has type unsigned long long int and size 8
2147483647 has type int and size 4
2147483648 has type long long int and size 8
2147483647+1 has type int and size 4
-2147483647 has type int and size 4
-2147483648 has type long long int and size 8
-2147483647-1 has type int and size 4
当我写下面的时候,它没有编译warning/error。
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的最小值,编译时不报warning。 长 universe_of_defects = -2147483648L;
根据文档,32 位可以容纳从 0 到 4,294,967,295(如果无符号)或从 -2147483648 到 +2147483647(如果有符号)。但它看起来像持有-2147483648 到 4,294,967,295。怎么可能?
感谢大家的见解。这是我想出来的。
long 变量的值从 -2147483648 到 +2147483647。所以它使用 32 位(4 字节)。语句“long universe_of_defects = 4294967295L;”编译期间不显示任何警告消息,并存储二进制值如下。
1111 1111 1111 1111 1111 1111 1111 1111
在二进制补码中,解释为-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 位整数二进制补码 0000_0000_0000_0000_1111_1111_1111_1111
,但 32 位整数没有表示(32 位整数从 -2147483648
到 2147483647
) 因此转换为 32 位有符号整数将 始终导致 实现定义的行为 。 64 位有符号的值 -4294967295L
二进制补码是 1111_1111_1111_1111_0000_0000_0000_0001
,这可能是当 “转换” 到 32 位(通过截断 32 个最高有效位)时,最终值导致正数 +1
(截断导致模式 0000_0000_0000_0001
或 +1
),这可以解释您从编译器收到的警告消息(您在转换中丢失了符号位,在在这种情况下,但如果编译器将第 63 位移动到位置 31,结果将是 1000_0000_0000_0001
,即 -2147483647
的二进制补码,与您使用的原始值无关在源代码中)
您必须编写可移植代码,并使用可以在您使用的类型之间转换的值。编译器只是在告诉您转换过程中发生了什么。
您的代码在这两种情况下都具有实现定义的行为,因为表达式在您的 32 位系统上具有类型 long long int
并且超出了类型 long
.
C 标准对此很清楚:
6.3.1.3 Signed and unsigned integers
- When a value with integer type is converted to another integer type other than
_Bool
, if the value can be represented by the new type, it is unchanged.- Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
- Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
混淆可能来自 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 位系统的输出(32 位 int
和 long
):
4294967295 has type long long int and size 8
4294967295U has type unsigned int and size 4
4294967295L has type long long int and size 8
4294967295UL has type unsigned long int and size 4
4294967295LL has type long long int and size 8
4294967295ULL has type unsigned long long int and size 8
0xffffffff has type unsigned int and size 4
0xffffffffU has type unsigned int and size 4
0xffffffffL has type unsigned long int and size 4
0xffffffffUL has type unsigned long int and size 4
0xffffffffLL has type long long int and size 8
0xffffffffULL has type unsigned long long int and size 8
2147483647 has type int and size 4
2147483648 has type long long int and size 8
2147483647+1 has type int and size 4
-2147483647 has type int and size 4
-2147483648 has type long long int and size 8
-2147483647-1 has type int and size 4