切换到新编译器后,sscanf的工作方式不同

问题描述 投票:4回答:1

我在两个不同版本的编译器中有不同的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-studio scanf
1个回答
2
投票

%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
}

Live on 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.是旧版本的错误?

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

  • %d %n被解释为在数字后需要空格,以便%n“生效”。您的输入“123456”没有以下空格,因此它将保留offset undefined。这违反了标准,因为它清楚地表明格式字符串中的空白字符与输入字符串中的零个或多个空格字符匹配。确切的措辞:“由白色空格字符组成的指令是通过读取第一个非空白字符(仍未读取)的输入来执行的,[..]该指令永远不会失败。” (N1570§7.21.6.2/5
  • 旧平台上的sizeof(int)太小,无法代表123456。 min允许的有符号整数的最大值是32767(感谢@MartinYork) 这意味着与sscanf%d只能读取12345但不能读取123456,因此将6留在输入中。这应该导致5的偏移 - 而不是4 - 但是!

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

© www.soinside.com 2019 - 2024. All rights reserved.