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