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

时间:2019-04-18 19:57:13

标签: c++ visual-studio scanf

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

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

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

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

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

1 个答案:

答案 0 :(得分: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种情况下如何计算两个附加的空白字符(所以偏移= 8),而在第4种情况下如何不对它们附加计数(所以offset = 6)。


  

我不明白为什么要使用Visual Studio 2013版本12.0 offset == 4和使用Visual Studio 2017版本15.9 offset == 6?这是旧版本的错误吗?

这确实似乎是一个错误。可能性:

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

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

      

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

    这意味着sscanf%d只能读取12345,而不能读取123456,因此将6保留在输入中。该应该导致5的偏移量-而不是4-的偏移量!


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