【问题标题】:How to make sure that fscanf() returns an overflow error for %ld?如何确保 fscanf() 返回 %ld 的溢出错误?
【发布时间】:2022-12-08 22:10:41
【问题描述】:

根据手册页,fscanf 在出错时返回 EOF,但如果溢出是错误,我无处可寻。 我听说 strtol() 可能与这个过程密切相关,它所做的是消耗文件中的所有数字,然后在下溢时返回 LONG_MIN,或在溢出时返回 LONG_MAX,并将 errno 设置为 ERANGE。但问题是它将错误传递给 fscanf()。

我不想消耗无限数量的数字,我想 100% 确保 fscanf() 在溢出的情况下返回 EOF。

我怎么做? %10ld

【问题讨论】:

  • “如果溢出是一个错误。” --> 对于fscanf("%ld", ..."%10ld",溢出是未定义行为(UB)。使用 "%9ld" 就没有溢出的可能,只有潜在的未读文本。
  • “它将错误传递给 fscanf()。” --> 没有。
  • “我想 100% 确保 fscanf() 在溢出的情况下返回 EOF” --> 没有 UB 是不可能的。
  • “我不想消耗无限多的数字,”--> 最多要消耗多少个数字?帖子有大约 4 个问题,也许可以清楚地确定最感兴趣的问题?标题问题的答案是:不可能。

标签: c


【解决方案1】:

由于整数溢出而返回 EOF 将违反 C 标准。标准未指定在整数溢出时将 errno 设置为 ERANGE,但允许库函数在成功时将 errno 设置为非零值。

据推测,在整数溢出时将 errno 设置为 ERANGE 的实现将允许调用者通过在调用之前设置 errno = 0; 来检测此错误:

    int val;
    int ret;

    errno = 0;
    ret = fscanf(fp, "%d", &val);
    if (ret == EOF) {
        /* Normal error. */
    } else if (ret == 0) {
        /* val not read. */
    } else {
        /* val read. */
        if (errno == ERANGE) {
            /* There was an overflow. */
        }
    }

在 glibc C 库(至少到版本 2.36)中,这对于窄于 long 的整数类型不能正常工作。例如,在我的 64 位 GNU/Linux 系统(64 位long 和 32 位int)上,将输入 12345678901 读取到 int 不会设置 errno,但读取输入 12345678901234567890进入 int 确实将 errno 设置为 ERANGE。不在整数溢出时设置 errno不是glibc 库中的错误。据我所知,在撰写本文时,唯一记录此错误的地方是从 3.01 版到至少 6.01 版的 Linux 手册页。以我的拙见,scanf(3) 手册页根本不应该提及 ERANGE


正如@chux 在问题 cmets 中提到的,转换无法由目标对象类型表示的输入数字会导致未定义的行为(除非使用了赋值抑制标志*)。参考C17 7.21.6.2/10(fscanf),7.29.2.2/10(fwscanf):

[...] 除非 * 指示分配抑制,否则转换结果将放置在尚未收到转换结果的格式参数之后的第一个参数指向的对象中。如果此对象没有合适的类型,或者转换的结果无法在对象中表示,则行为未定义。

使用scanf 读取整数而不仔细指定最大字段宽度的合规程序无法防止由不正确或恶意输入引起的未定义行为,这似乎很奇怪。可选的“附件K”功能,例如scanf_s允许将不正确的输入视为运行时约束违规的实现,但不要求它这样做。

“奇怪的游戏。唯一的胜利之招就是不玩。” ——约书亚/WOPR,战争游戏 (1983)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多