【问题标题】:Overflow occurs, but errno is not ERANGE. How does this happen?发生溢出,但 errno 不是 ERANGE。这是怎么发生的?
【发布时间】:2012-10-13 01:01:37
【问题描述】:

我正在尝试使用errno 来检测我是否执行了导致溢出的操作。然而,虽然我写了一个故意溢出的函数,errno == ERANGE 是假的。这是怎么回事?

代码如下:

#include <stdio.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    unsigned char c = 0;
    int i;

    for (i = 0; i< 300; i++) {
        errno = 0;
        c = c + 1;
        if (errno == ERANGE) {// we have a range error
            printf("Overflow. c = %u\n", c);
        } else {
            printf("No error. c = %u\n", c);
        }
    }
    return 0;
}

我预计这会在我们将 1 加到 255 时出现溢出错误,但没有错误。这是(截断的)输出:

No error. c = 245
No error. c = 246
No error. c = 247
No error. c = 248
No error. c = 249
No error. c = 250
No error. c = 251
No error. c = 252
No error. c = 253
No error. c = 254
No error. c = 255
No error. c = 0
No error. c = 1
No error. c = 2
No error. c = 3
No error. c = 4
No error. c = 5
No error. c = 6
No error. c = 7
No error. c = 8
No error. c = 9

有人可以解释为什么它没有检测到错误,以及我如何更改它或以其他方式制作一个可以检测我的值是否溢出的函数?注意:最终我想用long ints 来做这件事,所以不可能简单地将它转换成更大的数据类型。

编辑:

我已经找到了一些简单的函数来分别检测整数数据类型的加法和乘法溢出。它没有涵盖所有情况,但涵盖了很多情况。

乘法:

int multOK(long x, long y)
/* Returns 1 if x and y can multiply without overflow, 0 otherwise */
{
    long p = x*y;

    return !x || p/x == y;
}

签名添加:

int addOK(long x, long y)
/* Returns 1 if x and y can add without overflow, 0 otherwise */
{
    long sum = x+y;
    int neg_over = (x < 0) && (y < 0) && (sum >= 0);
    int pos_over = (x >= 0) && (y >= 0) && (sum < 0);
    return !neg_over && !pos_over;
}

无符号加法:

int unsignedAddOK(unsigned long x, unsigned long y)
/* Returns 1 if x and y can add without overflow, 0 otherwise */
{
    unsigned long sum = x+y;

    return sum > x && sum > y;
}

【问题讨论】:

  • 简单地递增不会抛出错误,因此不会设置errno。 函数(例如strtod)设置errno。
  • 另外,查看&lt;limits.h&gt; 以及this related question 可能会有所帮助。
  • 您可能希望使用一种检查整数溢出的语言(例如带有checked 的 Ada 或 C#)或透明地支持任意精度算术的语言(例如 Python、Lisp、Haskell 等。 )

标签: c overflow errno


【解决方案1】:

Errno 不是处理器设置的变量,它仅用于系统调用和一些标准函数来报告错误。

除了在每次循环迭代中验证 c &lt; c + 1 之外,我不知道任何检测您所问内容的技术。

编辑:经过一番研究,我发现this wikipedia 关于状态寄存器,这是指示此类错误的 CPU 标志。

【讨论】:

  • This 帖子解释了为什么没有标准状态寄存器检查。
【解决方案2】:

据我了解,errno 是由函数设置的。除非您创建自己的函数,否则它不会检测到附加溢出错误。您可以测试一下结果是否小于您的操作数之一。

#include <stdio.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    unsigned char c = 0, result = 0;
    int i;

    for (i = 0; i< 300; i++) {
        result = c + 1;
        if (result < c) {// we have a range error
            printf("Overflow. c = %u\n", result);
        } else {
            c = result;
            printf("No error. c = %u\n", c);
        }
    }
    return 0;
}

【讨论】:

  • 是的,但这并不能说明所有的可能性。例如,如果您添加一个本身超出溢出限制的值,例如尝试将 300 添加到一个字符,那么根据 c 的原始值,您可能仍然会得到一个大于 c 的数字,但是还是错了。
【解决方案3】:

除了别人提出的关于errno 的观点之外,您还使用unsigned char 作为计数器。无符号类型被明确定义为环绕。换句话说,当c 是8 位unsigned char 时,计算c=255; c=c+1; 不会出错:结果总是0

【讨论】:

    【解决方案4】:

    整数溢出是未定义的行为。未定义的行为不是可检测的条件。是终局。一旦调用了未定义的行为,就无法对程序的状态得出任何结论。

    除此之外,errno 与算术表达式无关。它用于报告库函数的错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-19
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      • 2010-12-15
      • 2016-12-03
      • 2020-12-11
      • 2019-03-06
      相关资源
      最近更新 更多