整数溢出与从long long到int的隐式转换

时间:2018-06-22 10:20:29

标签: c implicit-conversion integer-overflow

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,结果是实现定义的。

我认为未定义的行为会导致任何事情,包括崩溃,因此最好避免所有损失,因此第二个版本是可取的。但是我不确定我的推理是否还可以。

问题:第二个版本更可取,还是第一个版本更可取?

3 个答案:

答案 0 :(得分:2)

这两个选项都不好,因为它们不能产生期望的结果。恕我直言,这是试图按不良程度对它们进行排名的有争议的观点。

我的建议是针对所有用例修复功能明确的功能。

答案 1 :(得分:2)

如果您(程序员)将永远不会(永远!)将值传递给product()函数,这将导致未定义的行为,那么请选择第一个版本,为什么不行。
第二个版本返回结果的sizeof(int)*CHAR_BIT个最低有效位(这是实现定义的行为),并且在LLONG_MAX == INT_MAX的体系结构上仍然可能溢出。第二个版本可能需要很长的时间才能在8位处理器上执行,但对长长乘法的真正支持很差,也许您应该在将long long转换为intif (x > INT_MAX) return INT_MAX;时处理溢出,除非您只对乘积结果的最低有效位真正感兴趣。
最好的版本是不存在未定义行为的版本。如果不确定ab的乘积是否会导致不确定的行为,则应检查乘积是否会为这种情况做准备。

#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