Является ли scanf ( "% d% d", & x, & x) четко определенным?

Является ли следующий код корректным?

#include <stdio.h>

int ScanFirstOrSecond(const char *s, int *dest) {
    return sscanf(s, "%d%d", dest, dest);
}

int main(void) {
    int x = 4;
    ScanFirstOrSecond("5", &x);
    printf("%d\n", x);  // prints 5

    // Here is the tricky bit
    ScanFirstOrSecond("6 7", &x);
    printf("%d\n", x);  // prints 7
    return 0;
}

Другими словами, имеют ли аргументы ... им подразумеваемые restrict?

Наиболее подходящий C spec, который я нашел,

Функция fscanf выполняет каждую директиву формата по очереди.... C11dr §7.21.6.2 4

Ответы

Ответ 1

Короткий ответ: Да, он определен:

scanf попытается преобразовать последовательность байтов из stdin в виде целого числа, записанного в базе 10 с необязательными начальными пробелами и необязательным знаком. В случае успеха номер будет сохранен в x. scanf выполнит эти шаги второй раз. Возвращаемое значение может быть EOF, 0, 1 или 2, а для последнего 2 последнее преобразованное число будет сохранено в x.

Длительный ответ несколько более утончен:

Кажется, что в стандарте C указано, что значения хранятся в порядке строки формата. Цитирование стандарта C11:

7.21.6.2 Функция fscanf

...

4 Функция fscanf выполняет поочередно каждую директиву формата. Когда все директивы выполнены или если директива не работает (как описано ниже), функция возвращает.

...

7 Директива, являющаяся спецификацией преобразования, определяет набор совпадающих входных последовательностей, как описано ниже для каждого спецификатора. Спецификация преобразования выполняется в следующих шагах:

...

10 За исключением спецификатора %, входной элемент (или, в случае директивы %n, количество входных символов) преобразуется в тип, соответствующий спецификатору преобразования. Если входной элемент не соответствует последовательности, выполнение директивы не выполняется: это условие является совпадающим сбоем. Если исключение присваивания не было указано *, результат преобразования помещается в объект, на который указывает первый аргумент, следующий за аргументом формата, который еще не получил результат преобразования.

...

16 Функция fscanf возвращает значение макроса EOF, если сбой ввода происходит до того, как завершилось первое преобразование (если оно есть). В противном случае функция возвращает количество назначенных элементов ввода, которое может быть меньше, чем предусмотрено, или даже равно нулю, в случае раннего совпадения.

Нигде в этой спецификации нет доступа к указанным выше объектам вывода.

Однако формулировка Стандарта, по-видимому, указывает на то, что если 2 указателя указывают на один и тот же объект, поведение может быть неожиданным: результат преобразования помещается в объект, на который указывает первый аргумент, следующий за аргументом формата, который имеет еще не получил результат преобразования. Эта фраза несколько неоднозначна: к чему это еще не получил результат преобразования? объект или аргумент? Объекты получают результаты преобразования, а не аргументы указателя. В вашем искаженном примере объект x уже получил результат преобразования, поэтому он не должен получать еще один... Но, как отмечено supercat, эта интерпретация является откровенно ограничительной, поскольку это подразумевает, что все преобразованные значения будут сохранены в первый целевой объект.

Таким образом, он кажется полностью определенным и четко определенным, но формулировка спецификации может быть усовершенствована для устранения потенциальной двусмысленности.

Ответ 2

scanf() функции семейства выполняют указания, которые вы оставляете их в строке формата строго по очереди. Таким образом, первое значение будет считываться, а затем второе, перезаписывая первое. Здесь ничего нет.

Ответ 3

Да, четко определен. Это означает "прочитать первый токен в * dest, а затем прочитать второй токен в * dest снова". Это странно, но легально. Да, потому что sscanf() выполняет директивы в строке формата в строгом порядке.