当我进行以下计算时:
unsigned long long int data_size = 60123456 * 128 * sizeof(double);
printf("data_size= %llu \n", data_size);
我出乎意料地得到溢出警告:
test.c:20:49: warning: overflow in expression; result is -894132224 with type 'int' [-Winteger-overflow]
unsigned long long int data_size = 60123456 * 128 * sizeof(double);
^
1 warning generated.
我无法理解为什么即使我使用unsigned long long int
也会出现此错误!有人可以解释原因吗?谢谢
答案 0 :(得分:46)
在将值分配给变量之前发生溢出。为避免使用长长的后缀:
60123456LL * 128 * sizeof(double)
(评论还建议ULL这里没有必要,因为值在有符号范围内,但这是通用的方法)
答案 1 :(得分:18)
你转得太久了。 60123456 * 128
仍然是一个溢出的int表达式。
请尝试使用60123456LL * 128
。 (LL因为结果值不能保证适合长期。)
答案 2 :(得分:12)
unsigned long long int data_size = 60123456 * 128 * sizeof(double); // trouble-some code
目标类型unsigned long long int data_size =
与产品计算60123456 * 128 * sizeof(double)
无关。
最好确保使用至少目标类型的大小来完成数学运算以避免溢出。在OP的情况下,这意味着LLU
的常数。
有2个产品计算,每个都有自己的类型数学。
60123456
是int
或long
,具体取决于int
范围。我们假设它是int
。
60123456 * 128
是int * int
。数学乘积7695802368
超出32位有符号整数范围,因此有符号整数溢出或未定义行为(UB)与32位int
如果60123456 * 128
没有溢出,说64位int
,则下一次乘法将是* sizeof(double);
,因此int * size_t
会产生size_t
类型产品
产品计算应使用至少unsigned long long
数学,如下所示:
unsigned long long int data_size = 1LLU * 60123456 * 128 * sizeof(double);
// or
unsigned long long int data_size = 60123456LLU * 128 * sizeof(double);
// or
unsigned long long int data_size = 60123456;
data_size *= 128 * sizeof(double);
// or avoiding naked magic numbers
#define A_HEIGHT 60123456
#define A_WIDTH 128
unsigned long long int data_size = 1LLU * A_HEIGHT * A_WIDTH * sizeof(double);
sizeof (double)
暗示代码试图找到某些类似2D的结构的大小。我希望代码如下。请注意,sizeof
的结果类型为size_t
,因此产品数学运算至少使用size_t
数学。
size_t data_size = sizeof(double) * 60123456 * 128;
printf("data_size= %zu\n", data_size);
有关适用的详细信息,另请参阅Why write 1,000,000,000 as 1000*1000*1000 in C? 和我的回答reasons not to use 1000 * 1000 * 1000。
答案 3 :(得分:3)
常量60123456
和128
的类型为int
,因此表达式60123456 * 128
的类型为int
。此表达式的值将溢出int
的范围。在这种情况下,编译器能够检测到它,因为两个操作数都是常量。
您应该在第一个操作数上使用ULL
后缀来生成常量unsigned long long
的类型。这样它匹配分配的类型。然后,对于涉及此值的任何操作,在应用操作之前,另一个操作数将被提升为unsigned long long
,并且您不会有任何溢出。
所以结果表达式应如下所示:
unsigned long long int data_size = 60123456ULL * 128 * sizeof(double);
答案 4 :(得分:2)
词法分析器在翻译阶段7(将预处理标记转换为C标记)中附加常量类型。参见ISO9899 5.1.1.2 Translation phases
。正如其他人所说的那样,词法分析器将根据您的意愿在int
而不是unsigned long long
的常量上附加,并为default arithmetic conversions
生成unsigned long long
类型的常量在赋值的右侧,你需要告诉词法分析器在生成溢出的操作unsigned long long
中的至少一个常量上附加类型*
,所以写60123456ULL * 128
或60123456 * 128ULL
或两者。