Введение

В C и C++ точка последовательности определяет любую точку в исполнении компьютерной программы, в которой гарантируется, что все побочные эффекты предыдущих вычислений завершены, и никакие побочные эффекты последующих вычислений ещё не начались. Это ключевая концепция для определения корректности и, если корректны, возможных результатов выражений. Иногда добавление дополнительных точек последовательности необходимо для обеспечения корректности выражения и гарантированного порядка вычислений. Начиная со стандартов C11 и C++11, термин "точка последовательности" заменен на "последовательность вычислений". Существует три варианта:

Вычисление одного выражения может быть упорядочено перед вычислением другого выражения, или, эквивалентно, вычисление другого выражения упорядочено после вычисления первого. Вычисления выражений упорядочены недетерминированно, то есть одно вычисление упорядочено перед другим, но какое именно – не указано. Вычисления выражений не упорядочены. Выполнение неупорядоченных вычислений может перекрываться, что может привести к потенциально катастрофическому неопределённому поведению, если они используют общие данные. Эта ситуация может возникнуть как в параллельных вычислениях, вызывая гонки данных, так и в однопоточных программах. Например, `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 она присутствует только неявно.) В конце инициализатора; например, после вычисления в объявлении. Между каждым спецификатором в каждой последовательности спецификаторов; например, между двумя вычислениями в (Это не пример оператора-запятой.) После каждого преобразования, связанного со спецификатором формата ввода-вывода. Например, в выражении , есть точка последовательности после вычисления и перед печатью .

C11 и C++11

Частично из-за введения языковой поддержки многопоточности, C11 и C++11 ввели новую терминологию для порядка вычислений. Одна операция может быть "упорядочена перед" другой, или обе могут быть "неопределённо упорядочены" (одна должна завершиться перед другой), или "неупорядочены" (операции внутри каждого выражения могут выполняться перемежаясь).