Spec-Zone.ru › C

Порядок вычисления

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

В C нет понятия вычисления слева направо или справа налево; его не следует путать с ассоциативностью операторов слева направо и справа налево: выражение f1() + f2() + f3() разбирается как (f1() + f2()) + f3() из-за левой ассоциативности operator+, но во время выполнения вызов функции f3() может быть вычислен первым, последним или между f1() и f2().

Определения

Вычисления

Для каждого выражения или подвыражения компилятор выполняет два вида вычислений (оба из них необязательны):

  • вычисление значения : вычисление значения, возвращаемого выражением. Оно может включать определение идентичности объекта (вычисление lvalue) или чтение значения, ранее присвоенного объекту (вычисление rvalue).
  • побочный эффект : доступ (чтение или запись) к объекту, обозначенному lvalue типа volatile, изменение (запись) объекта, атомарная синхронизация(начиная с C11), изменение файла, изменение среды вычислений с плавающей точкой (если поддерживается) или вызов функции, выполняющей любую из этих операций.

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

Упорядочение

Предшествование в порядке следования — это асимметричное, транзитивное отношение между вычислениями в одном потоке (оно может распространяться на несколько потоков, если задействованы атомарные типы и барьеры памяти).

  • Если между подвыражениями E1 и E2 расположена точка следования, то вычисление значения и побочные эффекты E1 предшествуют в порядке следования каждому вычислению значения и каждому побочному эффекту E2.
  • Если вычисление A предшествует в порядке следования вычислению B, то вычисление A завершится до начала вычисления B.
  • Если A не предшествует в порядке следования B, а B предшествует в порядке следования A, то вычисление B завершится до начала вычисления A.
  • Если A не предшествует в порядке следования B и B не предшествует в порядке следования A, возможны два варианта:
    • вычисления A и B не упорядочены относительно друг друга: они могут выполняться в любом порядке и могут перекрываться (в рамках одного потока выполнения компилятор может чередовать инструкции ЦП, составляющие A и B)
    • вычисления A и B упорядочены неопределённым образом: они могут выполняться в любом порядке, но не могут перекрываться: либо A завершится до B, либо B завершится до A. При следующем вычислении того же выражения порядок может быть противоположным.
(начиная с C11)

Правила

1) После вычисления всех аргументов функции и указателя на функцию, но до фактического вызова функции, расположена точка следования.
2) После вычисления первого (левого) операнда и до вычисления второго (правого) операнда следующих бинарных операторов расположена точка следования: && (логическое И), || (логическое ИЛИ) и , (запятая).
3) После вычисления первого (левого) операнда и до вычисления второго или третьего операнда (в зависимости от того, какой из них вычисляется) условного оператора ?: расположена точка следования
4) После вычисления полного выражения (выражения, которое не является подвыражением: обычно это выражение, заканчивающееся точкой с запятой, или управляющая конструкция if/switch/while/do) и до следующего полного выражения расположена точка следования.
5) В конце полного декларатора расположена точка следования. 6) Непосредственно перед возвратом библиотечной функции расположена точка следования. 7) После действия, связанного с каждым спецификатором формата при форматированном вводе-выводе, расположена точка следования (в частности, допустимо, чтобы scanf записывала разные поля в одну и ту же переменную, а printf считывала и изменяла или изменяла одну и ту же переменную несколько раз с помощью %n) 8) Точки следования расположены до и непосредственно после каждого вызова функции сравнения, выполняемого библиотечными функциями qsort и bsearch, а также между любым вызовом функции сравнения и перемещением связанных объектов, выполняемым qsort (начиная с C99)
9) Вычисления значений (но не побочные эффекты) операндов любого оператора предшествуют вычислению значения результата оператора (но не его побочным эффектам). 10) Побочный эффект (изменение левого аргумента) оператора прямого присваивания и всех операторов составного присваивания следует за вычислением значений (но не побочными эффектами) обоих аргументов — левого и правого. 11) Вычисление значения операторов постфиксного инкремента и декремента предшествует их побочному эффекту. 12) Вызов функции, который не предшествует в порядке следования другому вызову функции и которому другой вызов функции также не предшествует в порядке следования, упорядочен с ним неопределённым образом (инструкции ЦП, составляющие разные вызовы функций, не могут чередоваться, даже если функции встроены) 13) В выражениях списка инициализации все вычисления упорядочены неопределённым образом 14) По отношению к вызову функции, упорядоченному неопределённым образом, операции операторов составного присваивания, а также префиксные и постфиксные формы операторов инкремента и декремента являются отдельными вычислениями. (начиная с C11)

Неопределённое поведение

1) Если побочный эффект над скалярным объектом не упорядочен относительно другого побочного эффекта над тем же скалярным объектом, поведение не определено.
i = ++i + i++; // undefined behavior
i = i++ + 1; // undefined behavior
f(++i, ++i); // undefined behavior
f(i = -1, i = -1); // undefined behavior
2) Если побочный эффект над скалярным объектом не упорядочен относительно вычисления значения с использованием значения того же скалярного объекта, поведение не определено.
f(i, i++); // undefined behavior
a[i] = i++; // undefined behavior
3) Приведённые выше правила применяются, если хотя бы один допустимый порядок вычисления подвыражений допускает такой неупорядоченный побочный эффект.

См. также

Приоритет операторов, определяющий, как выражения строятся на основе их представления в исходном коде.

Документация по C++ для Порядка вычисления

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/eval_order

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API