以int a=INT_MAX-1;
和int b=INT_MAX-1;
为例,并假设int是32位和一个函数
int product(int a,int b)
{
return a*b;
}
现在,产品a*b
溢出,导致标准中未定义的行为:
如果评估过程中发生异常情况 表达式(也就是说,如果结果没有在数学上定义,或者 不在其类型的可表示值的范围内) 未定义。
但是如果有的话
int product(int a,int b)
{
long long x = (long long)a*b;
return x;
}
然后假设this answer是正确的并且按照标准也适用于long long
,结果是实现定义的。
我认为未定义的行为会导致任何事情,包括崩溃,因此最好避免所有损失,因此第二个版本是可取的。但是我不确定我的推理是否还可以。
问题:第二个版本更可取,还是第一个版本更可取?
答案 0 :(得分:2)
这两个选项都不好,因为它们不能产生期望的结果。恕我直言,这是试图按不良程度对它们进行排名的有争议的观点。
我的建议是针对所有用例修复功能明确的功能。
答案 1 :(得分:2)
如果您(程序员)将永远不会(永远!)将值传递给product()
函数,这将导致未定义的行为,那么请选择第一个版本,为什么不行。
第二个版本返回结果的sizeof(int)*CHAR_BIT
个最低有效位(这是实现定义的行为),并且在LLONG_MAX == INT_MAX
的体系结构上仍然可能溢出。第二个版本可能需要很长的时间才能在8位处理器上执行,但对长长乘法的真正支持很差,也许您应该在将long long
转换为int
到if (x > INT_MAX) return INT_MAX;
时处理溢出,除非您只对乘积结果的最低有效位真正感兴趣。
最好的版本是不存在未定义行为的版本。如果不确定a
和b
的乘积是否会导致不确定的行为,则应检查乘积是否会为这种情况做准备。
#include <assert.h>
#include <limits.h>
int product(int a, int b)
{
assert(a < INT_MAX/b && b < INT_MAX/a);
if (!(a < INT_MAX/b && b < INT_MAX/a))
return INT_MAX;
return a * b;
}
或在GNUC中:
int product(int a, int b) {
int c;
if (__builtin_sadd_overflow(a, b, &c)) {
assert(0);
return INT_MAX;
}
return c;
}
答案 2 :(得分:0)
我相信稍作调整的第二版可能对您很有趣:
int product(int a, int b)
{
long long x = (long long)a * b;
if (x < INT_MIN || x > INT_MAX)
{
fprintf(stderr, "Error in product(): Result out of range of int\n");
abort();
}
return x;
}
此函数将两个整数作为长整数,计算其乘积并检查是否 结果在int范围内。如果是这样,我们可以从函数中返回它,而不会造成任何不良后果。如果不是,我们可以打印错误消息并中止操作,或者进行其他类型的异常处理。
编辑1:但是此代码stil期望(long long)a * b
不会溢出,这在i时不能保证。 e。 sizeof(long long) == sizeof(int).
在这种情况下,应添加溢出检查以确保不会发生这种情况。如果您不介意使用依赖于GCC的代码,那么(6.54) Integer Overflow Builtins对您可能会很有趣。如果您希望不使用任何扩展名停留在C语言中,那么还有一些方法可以检测乘法溢出,请参见此StackOverflow答案:https://stackoverflow.com/a/1815371/1003701