Порядок вычисления
Порядок вычисления операндов любого оператора C, включая порядок вычисления аргументов функции в выражении вызова функции, а также порядок вычисления подвыражений в любом выражении не определён (за исключением случаев, указанных ниже). Компилятор может вычислять их в любом порядке и может выбрать другой порядок при повторном вычислении того же выражения.
В C нет понятия вычисления слева направо или справа налево; его не следует путать с ассоциативностью операторов слева направо и справа налево: выражение f1() + f2() + f3() разбирается как (f1() + f2()) + f3() из-за левой ассоциативности operator+, но во время выполнения вызов функции f3() может быть вычислен первым, последним или между f1() и f2().
Определения
Вычисления
Для каждого выражения или подвыражения компилятор выполняет два вида вычислений (оба из них необязательны):
- вычисление значения : вычисление значения, возвращаемого выражением. Оно может включать определение идентичности объекта (вычисление lvalue) или чтение значения, ранее присвоенного объекту (вычисление rvalue).
-
побочный эффект : доступ (чтение или запись) к объекту, обозначенному lvalue типа
volatile, изменение (запись) объекта, атомарная синхронизация(начиная с C11), изменение файла, изменение среды вычислений с плавающей точкой (если поддерживается) или вызов функции, выполняющей любую из этих операций.
Если выражение не создаёт побочных эффектов и компилятор может определить, что его значение не используется, выражение может не вычисляться.
Упорядочение
Предшествование в порядке следования — это асимметричное, транзитивное отношение между вычислениями в одном потоке (оно может распространяться на несколько потоков, если задействованы атомарные типы и барьеры памяти).
- Если между подвыражениями
E1иE2расположена точка следования, то вычисление значения и побочные эффектыE1предшествуют в порядке следования каждому вычислению значения и каждому побочному эффектуE2.
| (начиная с C11) |
Правила
&& (логическое И), || (логическое ИЛИ) и , (запятая).?: расположена точка следования
5) В конце полного декларатора расположена точка следования. 6) Непосредственно перед возвратом библиотечной функции расположена точка следования. 7) После действия, связанного с каждым спецификатором формата при форматированном вводе-выводе, расположена точка следования (в частности, допустимо, чтобы scanf записывала разные поля в одну и ту же переменную, а printf считывала и изменяла или изменяла одну и ту же переменную несколько раз с помощью %n) 8) Точки следования расположены до и непосредственно после каждого вызова функции сравнения, выполняемого библиотечными функциями qsort и bsearch, а также между любым вызовом функции сравнения и перемещением связанных объектов, выполняемым qsort | (начиная с C99) |
| 9) Вычисления значений (но не побочные эффекты) операндов любого оператора предшествуют вычислению значения результата оператора (но не его побочным эффектам). 10) Побочный эффект (изменение левого аргумента) оператора прямого присваивания и всех операторов составного присваивания следует за вычислением значений (но не побочными эффектами) обоих аргументов — левого и правого. 11) Вычисление значения операторов постфиксного инкремента и декремента предшествует их побочному эффекту. 12) Вызов функции, который не предшествует в порядке следования другому вызову функции и которому другой вызов функции также не предшествует в порядке следования, упорядочен с ним неопределённым образом (инструкции ЦП, составляющие разные вызовы функций, не могут чередоваться, даже если функции встроены) 13) В выражениях списка инициализации все вычисления упорядочены неопределённым образом 14) По отношению к вызову функции, упорядоченному неопределённым образом, операции операторов составного присваивания, а также префиксные и постфиксные формы операторов инкремента и декремента являются отдельными вычислениями. | (начиная с C11) |
Неопределённое поведение
i = ++i + i++; // undefined behavior i = i++ + 1; // undefined behavior f(++i, ++i); // undefined behavior f(i = -1, i = -1); // undefined behavior
f(i, i++); // undefined behavior a[i] = i++; // undefined behavior
См. также
Приоритет операторов, определяющий, как выражения строятся на основе их представления в исходном коде.
| Документация по C++ для Порядка вычисления |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/eval_order