Sscanf работает по-другому после перехода на новый компилятор - PullRequest
4 голосов
/ 18 апреля 2019

У меня разное поведение функции 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?

1 Ответ

2 голосов
/ 19 апреля 2019

Чем %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. Это ошибка более старой версии?

Это действительно похоже на ошибку. Возможности:

  • %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, затем добавьте тестовый набор в свой набор тестов (у вас есть, не так ли?), Чтобы убедиться, Предоставленная функция соответствует вашим ожиданиям. Предоставьте собственную реализацию, если это необходимо.

...