Printf значение% n в одном вызове - бессмысленно?

Я не могу узнать намерение следующей части спецификации printf в cppreference.com:

После действия каждого преобразования есть точка последовательности спецификатор; это позволяет хранить несколько% n результатов в одном и том же переменная и печать значения, хранящегося в% n ранее в том же позвонить.

Это означает, что результат одного (или даже нескольких) %n спецификаторов (спецификаций) преобразования может быть распечатан в одном и том же printf -стоянии. Но я не могу понять, как это может быть достигнуто, поскольку все аргументы, переданные вызову printf, оцениваются до ввода тела printf (после оценки аргумента есть точка последовательности). Следовательно, значение переменной, к которой будет записываться a %n, оценивается до того, как printf имеет возможность перезаписать это значение переменной "количество символов, написанных до сих пор":

#include <stdio.h>

int main( int argc, char* argv[] )
{
    int n = 0;
    printf("Hello, world!%n (%d first n); %n (%d second n)", &n ,n, &n, n);
    // will print out "Hello, world! (0 first n);  (0 second n)"

    return 0;
}

Мой вопрос: Если нет возможности "напечатать значение, сохраненное% n ранее в рамках одного и того же вызова", не является ли соответствующая часть спецификации printf бессмысленной или вводящей в заблуждение?

Каков фактический смысл c99 standard:

7.19.6 Форматированные функции ввода/вывода (1) Отформатированные функции ввода/вывода должны вести себя так, как если бы после действия, связанные с каждым спецификатором.

Уменьшает ли "шансы" на получение поведения undefined?

Вопрос помечен С++ и c, потому что я думаю, что этот вопрос применяется одинаково для обоих языков.

Ответ 1

Ваш код действительно только печатает нули по причинам, которые вы правильно определили.

Утверждение в Стандарте по-прежнему необходимо из-за общей формулировки в другом месте, что поведение программы undefined, если объект записывается более одного раза без промежуточной точки последовательности. По сути, утверждение необходимо сказать, что ваш код не имеет поведения undefined (в отличие, скажем, i = i++;).

Ответ 2

Это может быть сумасшедшим, но я думаю, что это законно:

char s[2];
s[1] = '\0';
printf("Hi, world!%hhn%s", s, s);

%hhn принимает указатель на char. Он записывает 10 (количество написанных до сих пор символов) до s[0]. Затем он печатает строку s, которая эквивалентна "\n" или (char[]){ 10, 0 } (при условии, что ASCII).

Ответ 3

Можно представить, что компилятор переводит вызов printf в последовательность индивидуальных вызовов на fputs() со строковыми фрагментами, вычисленными по вызовам обработчиков преобразования. Эта реализация может сохранять значения в n до того, как будет напечатано значение n. Не будет ли это соответствовать?

Современные компиляторы уже выполняют небольшие оптимизации на printf(), такие как преобразование printf("Hello world\n"); в puts("Hello world"); и printf("\n"); в fputchar('\n');. Они также проверяют строку формата и согласованность аргументов... Дальнейшие оптимизации приведут к вышесказанному.