Является ли каждое преобразование, связанное с указателем формата ввода / вывода, точкой последовательности в C? - PullRequest
0 голосов
/ 02 июля 2018

Запись в Википедии о точках последовательности в C имеет эту точку последовательности:

После каждого преобразования, связанного с форматом ввода / вывода спецификатор. Например, в выражении printf ("foo% n% d", & a, 42), после оценки% n и до печать 42

Но стандарт C также говорит:

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

Эти две точки кажутся противоречивыми по природе для кодов, подобных этому

int i=1;
printf("%d, %d and %d\n", i++, i++, i--); 

Согласно записи в википедии после каждого спецификатора формата есть точка последовательности, поэтому она будет напечатана следующим образом: 1,2,3

Но в соответствии с неопределенной записью beaviour в стандарте C, он может печатать что угодно.

Ответы [ 3 ]

0 голосов
/ 02 июля 2018

Страница Википедии плохо сформулирована. Фактический текст (C11 7.21.6):

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

Эти действия все происходят после точки последовательности, описанной вашей цитатой «точка последовательности существует перед фактическим вызовом». Аргументы и постфиксное выражение оцениваются; затем функция введена; затем действия, указанные в строке формата, выполняются по порядку.

Например, действие, связанное с %i, выводит значение аргумента. Вы, кажется, путаете «вывод аргумента» с «вычислением аргумента»; последнее происходит до входа в функцию.

0 голосов
/ 02 июля 2018

Оба правила верны. Вы не правильно их читаете.

Первый, второй -

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

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

Из-за этого звонка типа -

printf("%d, %d and %d\n", i++, i++, i--); 

не определен и вызывает UB.

Теперь для первого,

После каждого преобразования, связанного со спецификатором формата ввода / вывода. Например, в выражении printf ("foo% n% d", & a, 42) есть точка последовательности после оценки% n и перед печатью 42

Речь идет не об оценке параметров, а о фактической печати. Всякий раз, когда каждый спецификатор формата, такой как %d, %c, %n оценивается (как для scanf, так и printf), он может иметь побочные эффекты. Эти эффекты упорядочены в порядке появления спецификатора формата в строке.

Например

scanf("%d %d", &a, &b); // a and b are integers

Сохранение до a будет выполнено последовательно перед сохранением до b

Аналогично в случае printf, как указано в примере,

printf("foo %n %d", &a, 42);

Обновление до a будет выполнено последовательно перед печатью 42.

Нет никакого отношения к оценке 42, &a, printf или "foo %n %d".

0 голосов
/ 02 июля 2018

Строка формата является аргументом функции, и, таким образом, этот аргумент (строка) оценивается перед вызовом функции. Здесь нет особой магии, но все параметры для printf оцениваются в неопределенном порядке, как и для любой функции. (Код, такой как printf(ptr, ptr+n);, поэтому является подозрительным и вызывает плохо определенное поведение.)

Тогда перед вызовом функции есть точка последовательности.

Википедия говорит, что "есть точка последовательности после оценки% n и до печати 42". Это нечто совсем другое. Существует специальная гарантия того, что %n, как часть строки формата, вычисляется до того, как будет напечатан параметр, соответствующий этому спецификатору. Формально это указано в C11:

7.21.6 Отформатированные функции ввода / вывода

Форматированные функции ввода / вывода должны вести себя так, как если бы после действий, связанных с каждым спецификатором, была точка последовательности. 274)

Это не имеет ничего общего с оценкой аргументов функции. Итак, примерно, учитывая этот код:

printf("%d", i)

у вас будет следующий порядок исполнения:

  • "%d" и i оцениваются в неопределенном порядке до вызова функции.
  • Точка последовательности.
  • Функция вызывается.
  • Строка анализируется, и часть %d найдена и интерпретирована.
  • Точка последовательности.
  • i напечатано.
  • Точка последовательности.
  • Функция возвращает.

Эта специальная точка последовательности после спецификаторов формата, по-видимому, существует именно благодаря спецификатору %n, который записывает в память в соответствии с примечанием ноги:

274) Функции fprintf выполняют запись в память для спецификатора% n.

...