首页 > 解决方案 > sscanf 切换到新编译器后工作方式不同

问题描述

我在两个不同版本的编译器中有不同的 scanf 函数行为。

    int number;
    int offset = 0;
    const char* ref = "123456";
    sscanf(ref, "%d %n", &number, &offset);

我不明白为什么 Visual Studio 2013 版本 12.0 偏移 == 4 和 Visual Studio 2017 版本 15.9 偏移 == 6。它是旧版本的错误吗?当我删除空间时,两个版本都显示正确的数字:

    sscanf(ref, "%d%n", &number, &offset);

%d%n 与 %d %n 有何不同?

标签: c++visual-studioscanf

解决方案


有什么%d%n不同%d %n

第一个只计算 消耗的字符%d,而后者也计算任何可能跟随的空白字符。

#include <cstdio>
#include <iostream>


void test(const char * ref, const char * fmt)
{
    std::cout << "fmt: \"" << fmt << "\"  ref: \"" << ref << "\"" << std::endl;
    int number;
    int offset = 0;
    int ret = std::sscanf(ref, fmt, &number, &offset);
    std::cout << " - Returned: " << ret << "  Offset: " << offset << "  Number: " << number << std::endl;
}


int main(int argc, char* argv[])
{
    test("123456", "%d %n");
    test("123456", "%d%n");
    test("123456  ", "%d %n"); // case 3
    test("123456  ", "%d%n");  // case 4
}

住在ideone上);输出:

fmt: "%d %n"  ref: "123456"
 - Returned: 1  Offset: 6  Number: 123456
fmt: "%d%n"  ref: "123456"
 - Returned: 1  Offset: 6  Number: 123456
fmt: "%d %n"  ref: "123456  "
 - Returned: 1  Offset: 8  Number: 123456
fmt: "%d%n"  ref: "123456  "
 - Returned: 1  Offset: 6  Number: 123456

请注意在案例 3 中如何计算 2 个附加的空白字符(因此偏移量 = 8),而在案例 4 中它们不被计算在内(因此偏移量 = 6)。


我不明白为什么 Visual Studio 2013 版本 12.0 偏移 == 4 和 Visual Studio 2017 版本 15.9 偏移 == 6。它是旧版本的错误吗?

这确实看起来像一个错误。可能性:

  • %d %n被解释为在数字后需要空格%n才能“生效”。您的输入“123456”没有后续空格,因此offset未定义。这违反了标准,因为它清楚地表明格式字符串中的空白字符匹配输入字符串中的零个或多个空白字符。确切的措辞:“由空白字符组成的指令通过读取输入直到第一个非空白字符(仍然未读取)来执行,[..] 该指令永远不会失败。” ( N1570§7.21.6.2/5 )

  • sizeof(int)平台上的 太小,无法代表数字123456

    最小允许有符号整数的最大值是32767(感谢@MartinYork)

    这意味着sscanfwith %dcan only read12345但 not 123456,因此将 the 留6在输入中。这应该导致偏移量为 5 - 而不是 4 - 不过!


解决方案:确定您是否需要%d%n%d %n首先需要语义,然后将测试用例添加到您的测试套件(您有,不是吗?)以确保提供的功能符合您的期望。如果需要,提供自己的实现。


推荐阅读