即使使用unsigned long int也会出现溢出

时间:2017-07-24 13:17:13

标签: c

当我进行以下计算时:

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也会出现此错误!有人可以解释原因吗?谢谢

5 个答案:

答案 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个产品计算,每个都有自己的类型数学。

60123456intlong,具体取决于int范围。我们假设它是int

60123456 * 128int * 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)

常量60123456128的类型为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 * 12860123456 * 128ULL或两者。