【问题标题】:Integer overflow vs implicit conversion from long long to int整数溢出与从 long long 到 int 的隐式转换
【发布时间】:2018-12-01 19:21:40
【问题描述】:

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

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

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

【问题讨论】:

  • 除非你从第二个版本返回 long long 并将返回的值分配给 long long 类型,否则你仍然有溢出。
  • 一个会导致 UB,一个不会...我会说它们都很糟糕。不太确定你在这里期待什么样的答案
  • @taskinoor 实际上在第二种情况下它超出了范围分配,而不是溢出(参见问题链接的答案)
  • @MM 对,我错了。我同意你,他们两个都是坏人。
  • 猜猜这取决于用例。如果使用的是total_charge = product(unit_price, items_sold);,而您从发票中扣除了数十亿美元,我宁愿程序崩溃也不愿破产。 :-)

标签: c implicit-conversion integer-overflow


【解决方案1】:

这两个选项都不好,因为它们没有产生预期的结果。恕我直言,试图按坏顺序对它们进行排名是一个有争议的问题。

我的建议是修复函数​​,以便为所有用例明确定义。

【讨论】:

    【解决方案2】:

    如果您(程序员)永远不会(永远!)将值传递给product() 函数会导致未定义的行为,那么第一个版本,为什么不呢。
    第二个版本返回结果的sizeof(int)*CHAR_BIT 最低有效位(这是实现定义的行为),并且仍然可能在LLONG_MAX == INT_MAX 的架构上溢出。第二个版本可能需要很长时间才能在 8 位处理器上执行,而且对长长乘法的支持真的很差,也许你应该在将 long long 转换为 int 和一些 if (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;
    }
    

    【讨论】:

    • 转换为结果超出范围的有符号类型通常使用最低有效位完成,但从技术上讲,该行为是实现定义的。
    【解决方案3】:

    我相信稍作调整的第二个版本可能会让您感兴趣:

    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: 但是这段代码仍然期望 (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

    【讨论】:

      猜你喜欢
      • 2012-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多