Поскольку этот вопрос был задан из комментария на основе обсуждения here, я приведу некоторый контекст:
первый комментарий: Порядок операций не гарантированно порядок который вы передаете аргументам функции. Некоторые люди (ошибочно) предполагают, что аргументы будут оцениваться справа налево, но в соответствии со стандартом поведение не определено.
ОП принимает и понимает это. Нет смысла повторять тот факт, что your_function(++i, ++i)
является UB.
В ответ на это замечание: Благодаря Вашему комментарию, я вижу, что printf
может быть оценен в любом порядке, но я понял, что быть потому printf
аргументами являются частью va_list
. Вы говорите, что аргументы любой функции выполняются в произвольном порядке?
ОП просят разъяснить, поэтому я разработал немного:
Второй комментарий: Да, это именно то, что я говорю. даже вызов int your_function(int a, int b) { return a - b; }
не гарантирует, что переданные вами выражения будут оцениваться слева направо.Нет точки последовательности (точка, в которой выполняются все побочные эффекты предыдущих оценок). Возьмите this example. Вложенный вызов является точкой последовательности, поэтому внешний вызов проходит i+1
(13) и возвращаемое значение внутреннего вызова (неопределенный, в данном случае -1, потому что i++
, i
оценивает 12, 13, по-видимому), , но нет что это всегда будет иметь место
Это совершенно ясно, что эти конструкции позволяют запускать UB для всех функций.
Википедия спутанность
OP Котировки это:
После действия, связанного с форматом преобразования ввода/вывода спецификатора. Например, в выражении Е («Foo% N% D», & а, 42), существует точка последовательности после% N вычисляется перед печатью 42.
Затем применяет его к его фрагменту (prinf("%d - %d - %d\n", i, your_function(++i, ++i), i);
), вызывая спецификаторы формата, чтобы служить точками последовательности.
Что называется: «спецификатор формата ввода-вывода» - спецификатор %n
. Соответствующий аргумент должен быть быть указателем на целое число без знака, и ему будет присвоено количество печатаемых до сих пор символов. Естественно, %n
должен быть оценен до того, как остальная часть аргументов будет напечатана. Однако, используя указатель, переданный для %n
в других аргументов по-прежнему опасно: это не UB (ну, это не так, но это может быть):
printf("Foo %n %*s\n", &a, 100-a, "Bar");//DANGER!!
Существует точка последовательности перед тем функция вызывается, поэтому выражение 100-a
будет оценено до до того, как %n
установил &a
на правильное значение. Если a
неинициализирован, то 100-a
- UB. Если a
инициализируется равным 0, например, результат выражения будет равен. В целом, этот код в основном вызывает проблемы. Рассматривайте это как очень плохую практика или хуже ...
Просто смотрите на выходе генерируется либо одной из этих утверждений:
unsigned int a = 90;
printf("%u %n %*s\n",a, &a, 10, "Bar");//90 Bar
printf("%u\n", a);//3
printf("Foo %u %n %*s\n",a, &a, 10-a, "Bar");//Foo 3 Bar < padding used: 10 - 3, not 10 - 6
printf("%u\n", a);//6
В как вы можете видеть, n
получает переназначен внутри из printf
, поэтому вы не можете использовать его новое значение в списке аргументов (потому что есть точка последовательности). Если вы ожидаете, что n
будет переназначен «на месте», вы, по сути, ожидаете, что C выпрыгнет из вызова функции, оценит другие аргументы и вернется в вызов. Это просто невозможно. Если бы вы изменили unsigned int a = 90;
на unsigned int a;
, тогда поведение не определено.
О 12
«ы
Теперь, потому что ОП читать на точках последовательности, он правильно замечает, что это заявление:
printf("%d - %d - %d\n", i, your_function(++i, ++i), i);
немного отличается: your_function(++i, ++i)
является точка последовательности и гарантии, что i
будет увеличиваться в два раза. Этот вызов функции является точкой последовательности, потому что:
Прежде чем функция будет введена в вызов функции. Порядок, в котором вычисляются аргументы не указаны, но эта точка последовательности означает, что все их побочные эффекты являются полными, прежде чем функция вводится
Это означает, что перед printf
называется, your_function
имеет к (потому что его возвращаемое значение является одним из аргументов для вызова printf
), а i
будет увеличиваться в два раза.
Это может объяснить выход которого «12 - 0 - 12», но он гарантированно будет вывод?
Нет
Технически, хотя большинство компиляторов будут оценивать your_function(++i, ++i);
вызов первым, стандарт позволит компилятору оценить аргументы, передаваемые sprintf
слева направо (порядок не определен в конце концов). Так что это было бы равноценны результат:
10 - 0 - 12
//or even
12 - 0 - 10
//and
10 - 0 - 10
//technically, even this would be valid
12 - 0 - 11
Хотя последний выход крайне маловероятно (это было бы очень неэффективно)
Согласно [это] (http://stackoverflow.com/questions/ 4176328/undefined-behavior-and-sequence-points) это неопределенное поведение. Я также считаю, что 'your_function (++ i, ++ i)' также является неопределенным поведением. – NathanOliver
'your_function (++ i, ++ i)' явно UB. –
Последовательность точек, описанных стандартной цитатой, находится внутри тела 'printf()' после того, как она была вызвана. У вас есть экстремальное неопределенное поведение в вызывающей последовательности перед вызовом функции 'printf()', что означает, что любой результат является приемлемым (включая тот, который вы получили). –