Часто этот вопрос связан как дубликат вопросов, связанных с кодом, например
printf("%d %d\n", i, i++);
или
printf("%d %d\n", ++i, i++);
или аналогичные варианты.
Хотя это также неопределенное поведение , как уже говорилось, существуют незначительные различия, когда printf()
присутствует при сравнении с таким утверждением, как:
x = i++ + i++;
В следующем утверждении:
printf("%d %d\n", ++i, i++);
порядок оценки аргументов в printf()
равен не указан . Это означает, что выражения i++
и ++i
могут быть вычислены в любом порядке. C11 стандарт имеет несколько соответствующих описаний:
Приложение J, неопределенное поведение
Порядок, в котором указатель функции, аргументы и
подвыражения в аргументах оцениваются в вызове функции
(6.5.2.2).
3.4.4, неопределенное поведение
Использование неопределенного значения или другое поведение, когда это
Международный стандарт предоставляет две или более возможностей и налагает
никаких дальнейших требований, которые выбраны в любом случае.
ПРИМЕР. Примером неопределенного поведения является порядок, в котором
аргументы функции оцениваются.
неопределенное поведение само по себе НЕ является проблемой. Рассмотрим этот пример:
printf("%d %d\n", ++x, y++);
Это также имеет неопределенное поведение , потому что порядок оценки ++x
и y++
не указан. Но это совершенно законное и обоснованное утверждение. В этом выражении есть нет неопределенного поведения. Поскольку изменения (++x
и y++
) выполняются для различных объектов.
Что делает следующее утверждение
printf("%d %d\n", ++i, i++);
как неопределенное поведение означает, что эти два выражения изменяют один и тот же объект i
без промежуточной точки последовательности .
Другая деталь в том, что запятая , используемая в вызове printf (), является разделителем , а не оператором запятой .
Это важное различие, потому что оператор запятой вводит точку последовательности между оценкой их операндов, что делает следующее допустимым:
int i = 5;
int j;
j = (++i, i++); // No undefined behaviour here because the comma operator
// introduces a sequence point between '++i' and 'i++'
printf("i=%d j=%d\n",i, j); // prints: i=7 j=6
Оператор запятой оценивает свои операнды слева направо и выдает только значение последнего операнда. Таким образом, j = (++i, i++);
, ++i
увеличивает i
до 6
и i++
возвращает старое значение i
(6
), которое присваивается j
. Тогда i
становится 7
из-за постинкремента.
Так что, если запятая в вызове функции должна быть оператором запятой, то
printf("%d %d\n", ++i, i++);
не будет проблемой. Но он вызывает неопределенное поведение , потому что запятая здесь представляет собой разделитель .
Для новичков в неопределенное поведение будет полезно прочитать Что каждый программист C должен знать о неопределенном поведении , чтобы понять концепцию и многие другие варианты неопределенного поведения в C.
Этот пост: Неопределенное, неопределенное и определяемое реализацией поведение также имеет отношение.