Введение
В C и C++ точка последовательности определяет любую точку в исполнении компьютерной программы, в которой гарантируется, что все побочные эффекты предыдущих вычислений завершены, и никакие побочные эффекты последующих вычислений ещё не начались. Это ключевая концепция для определения корректности и, если корректны, возможных результатов выражений. Иногда добавление дополнительных точек последовательности необходимо для обеспечения корректности выражения и гарантированного порядка вычислений. Начиная со стандартов C11 и C++11, термин "точка последовательности" заменен на "последовательность вычислений". Существует три варианта:
In C and C++, a sequence point defines any point in a computer program's execution at which it is guaranteed that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed. They are a core concept for determining the validity of and, if valid, the possible results of expressions. Adding more sequence points is sometimes necessary to make an expression defined and to ensure a single valid order of evaluation. With C11 and C++11, usage of the term sequence point has been replaced by sequencing. There are three possibilities:
Вычисление одного выражения может быть упорядочено перед вычислением другого выражения, или, эквивалентно, вычисление другого выражения упорядочено после вычисления первого. Вычисления выражений упорядочены недетерминированно, то есть одно вычисление упорядочено перед другим, но какое именно – не указано. Вычисления выражений не упорядочены. Выполнение неупорядоченных вычислений может перекрываться, что может привести к потенциально катастрофическому неопределённому поведению, если они используют общие данные. Эта ситуация может возникнуть как в параллельных вычислениях, вызывая гонки данных, так и в однопоточных программах. Например, `a[i] = i++;` (где `a` – массив, а `i` – целое число) имеет неопределённое поведение.
Примеры двусмысленности
Рассмотрим две функции f и g. В C и C++ оператор + не связан с точкой последовательности, и поэтому в выражении f + g возможно, что сначала будет выполнена либо f, либо g. Оператор запятой вводит точку последовательности, и поэтому в коде f, g порядок вычисления определен: сначала вызывается f, а затем g. Точки последовательности также играют роль, когда одна и та же переменная модифицируется более одного раза в рамках одного выражения. Часто приводимым примером является выражение i = i++, которое, по-видимому, одновременно присваивает переменной i её предыдущее значение и увеличивает её. Окончательное значение i неоднозначно, поскольку, в зависимости от порядка вычисления выражения, увеличение может произойти до, после или вперемешку с присваиванием. Определение конкретного языка может специфицировать одно из возможных поведений или просто указывать, что поведение не определено. В C и C++ вычисление такого выражения приводит к неопределенному поведению. Другие языки, такие как C#, определяют приоритет оператора присваивания и инкремента таким образом, что результат выражения i = i++ гарантирован.
До C++03
В C и C++ точки последовательности возникают в следующих местах. (В C++ перегруженные операторы действуют как функции, и, таким образом, перегруженные операторы вводят точки последовательности так же, как и вызовы функций.) Между вычислением левого и правого операндов && (логического И), || (логического ИЛИ) (в рамках вычисления с коротким замыканием) и операторов-запятых. Например, в выражении , все побочные эффекты подвыражения завершаются до любой попытки доступа. Между вычислением первого операнда троичного условного оператора и его вторым или третьим операндом. Например, в выражении есть точка последовательности после первой , что означает, что она уже была увеличена к тому времени, когда выполняется второй экземпляр. В конце полного выражения. Эта категория включает в себя операторные выражения (такие как присваивание ), операторы return, управляющие выражения операторов , , , или , и каждое из трех выражений в операторе for. Перед входом в функцию при вызове функции. Порядок, в котором вычисляются аргументы, не определен, но эта точка последовательности означает, что все их побочные эффекты завершены до входа в функцию. В выражении , вызывается с параметром исходного значения , но увеличивается перед входом в тело . Аналогично, и обновляются перед входом в и соответственно. Однако не определено, в каком порядке выполняются , , , и в каком порядке увеличиваются . Если тело функции обращается к переменным и , оно может обнаружить, что оба, ни один или только один из них были увеличены. (Вызов функции не является использованием оператора-запятой; порядок вычисления для , , и не определен.) При возврате из функции, после того как возвращаемое значение скопировано в контекст вызывающей функции. (Эта точка последовательности указана только в стандарте C++; в C она присутствует только неявно.) В конце инициализатора; например, после вычисления в объявлении. Между каждым спецификатором в каждой последовательности спецификаторов; например, между двумя вычислениями в (Это не пример оператора-запятой.) После каждого преобразования, связанного со спецификатором формата ввода-вывода. Например, в выражении , есть точка последовательности после вычисления и перед печатью .
At the end of an initializer; for example, after the evaluation of in the declaration Between each declarator in each declarator sequence; for example, between the two evaluations of in (This is not an example of the comma operator.) After each conversion associated with an input/output format specifier. For example, in the expression , there is a sequence point after the is evaluated and before printing .
C11 и C++11
Частично из-за введения языковой поддержки многопоточности, C11 и C++11 ввели новую терминологию для порядка вычислений. Одна операция может быть "упорядочена перед" другой, или обе могут быть "неопределённо упорядочены" (одна должна завершиться перед другой), или "неупорядочены" (операции внутри каждого выражения могут выполняться перемежаясь).