You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何GCC中scanf解析"2E"为浮点数前缀却不解析"."?

*说明:*最初的问题不针对特定编译器,假设用于实验的GCC行为完全正确,且非空的匹配输入序列前缀不会导致匹配失败或输入失败。但后来发现(参考C17草案7.21.6.2 ¶10),答案更可能源于编译器/库bug,而非匹配前缀定义和处理的复杂性。为保留问题原意,仅做了保守修改(因此原文假设仍体现在后半部分)。

基于此,本文仍有一个未解决的问题:在文末的%4c示例中,将CD写入q[]是否合规。


浮点常量与scanf的行为差异

根据C17草案6.4.4.2 ¶1,2E0(对应值2.0)和.5(对应值0.5)是有效的浮点常量,而2E和.不是。

但在GCC环境下,scanf会将2E解析为2.0,却不对.做任何解析:

#include <stdio.h>

int main(void) {
    float fl;
    char c;

    printf("Please enter a floating-point number: ");
    if (scanf("%f", &fl) == 1)
        printf("<%0.2f>\n", fl);
    if (scanf("%c", &c) == 1)
        printf("[%c]\n", c);

    return 0;
}

预期用法:

Please enter a floating-point number: 123.4qrst
<123.40>
[q]

此处q作为占位字符,用于演示前一次scanf调用消耗了多少输入缓冲区内容。若仅输入浮点数,c会存储换行符:

Please enter a floating-point number: 123.4
<123.40>
[
]

尝试将2E和.解析为float类型:
在GCC(12.2.0,Windows平台MinGW)下,编译选项为gcc -std=c17 -pedantic -Wall -Wextra,运行结果如下:

Please enter a floating-point number: 2Eq
<2.00>
[q]
Please enter a floating-point number: .q
[q]

在MSVC(19.35.32217.1)下,编译选项为cl /std:c17 /Wall,运行结果如下:

Please enter a floating-point number: 2Eq
[q]
Please enter a floating-point number: .q
[q]

(暂不纠结字符串.应代表0还是1这类问题。)


基于C标准的分析

相关条款参考C17草案7.21.6.2 ¶9:

除非格式说明包含n转换说明符,否则会从流中读取输入项。输入项定义为不超过指定域宽的最长字符序列,且该序列是匹配输入序列或其前缀²⁹¹)。输入项后的首个字符(若存在)会保留未读取状态。若输入项长度为0,则指令执行失败;该情况属于匹配失败,除非文件结束、编码错误或读取错误导致无法从流中输入,此时属于输入失败。

²⁹¹) fscanf最多会将一个输入字符回推到输入流中。因此,某些可被strtod、strtol等函数接受的序列无法被fscanf接受。

据标准中与strtod及其同类函数(此处为strtof)相关的7.22.1.3 ¶3条款可知,2E0(2.0)和.5(0.5)是有效的「主题序列」(符合7.22.1.3 ¶2的定义),而2E和.不是。

若对7.21.6.2 ¶9条款理解正确,只有长度为0的输入项才会导致匹配失败或输入失败。因为2E和.都是匹配输入序列的有效前缀(尽管不是完整的匹配输入序列),所以两者都不属于匹配失败或输入失败。

由此产生疑问:为何GCC中的scanf会将2E解析为浮点数,却不解析.?

  • 这可能与「匹配输入序列的前缀」定义的细节有关。
  • 也可能与7.22.1.3 ¶3条款的细节(涉及strtod/strtof/strtold)相关,因为这些细节与scanf及其同类函数的最多1个字符回推限制有关。

%4c示例的合规性疑问

以下代码用于演示「匹配输入序列的前缀」的概念:

#include <stdio.h>

int main(void) {
    char p[5] = "pppp", q[5] = "qqqq";
    int i;

    i = sscanf("ABCD", "%2c%4c", p, q);
    printf("<%s>\n", p);  /* <ABpp> */
    printf("<%s>\n", q);  /* <CDqq> */
    printf("%d\n", i);  /* 2 (GCC), 1 (MSVC) */

    return 0;
}

(注意:GCC和MSVC返回的赋值项数量不同,尽管两者都将AB写入p[]、CD写入q[]。)

此处,尽管%4c(对应q)要求匹配恰好4个字符(参考C17草案7.21.6.2 ¶12中关于c的条款):

匹配恰好指定域宽的字符序列(若格式说明中未指定域宽则为1个字符)。[fn]

CD是匹配输入序列的有效前缀,因此该代码不会导致匹配失败或输入失败。(鉴于赋值长度可能小于指定域宽,对标准中使用「恰好」一词感到困惑。)

由此提出问题:若GCC或MSVC行为不合规,上述%f示例和%4c示例的正确输出应该是什么?


内容的提问来源于stack exchange,提问作者Lover of Structure

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:25:59