Interested Article - Точка следования

Точка следования ( англ. sequence point ) — в программировании любая точка программы, в которой гарантируется, что все побочные эффекты предыдущих вычислений уже проявились, а побочные эффекты последующих ещё отсутствуют.

Точки следования часто упоминают, когда речь идёт о языках C и C++ . В этих языках можно записать выражение, порядок вычисления подвыражений которого не определён стандартами и влияет на результат. Добавление одной или нескольких точек следования позволяет гарантировать порядок вычисления в некоторых случаях.

Стоит заметить, что подход к упорядочению процесса вычисления выражений на основе точек следования изначально достаточно хорошо отвечал потребностям языка C, но не являлся для языка C++, в котором был существенно расширен набор операторов, возвращающих lvalue результаты. А с появлением необходимости языковой поддержки многопоточности в языках C и C++ от упорядочения на основе точек следования пришлось отказаться полностью. Современные спецификации языков C и C++ описывают упорядочение процесса вычисления выражений через отношения упорядочено до ( sequenced before ) и упорядочено после ( sequenced after ). Начиная со стандарта C++11 , в языке C++ больше не существует понятия точки следования . В языке С понятие точки следования сохранилось по сей день, но, начиная со стандарта C11 , не как фундаментальная концепция, а лишь как комбинация отношений упорядочено до и упорядочено после .

Стандарт C++11 , а также последующие стандарты C++14 и C++17 внесли в операторы языка C++ большое количество дополнительных упорядочений на основе новой модели, что привело к тому, что многие выражения, поведение которых являлось неопределенным в C++98 , получили вполне определённое поведение в современном C++. На сегодняшний день строгость упорядочения процесса вычисления выражений в языке C++ существенно превосходит таковую в языке C.

Примеры неоднозначности в языках C и C++

При наличии неоднозначностей стандарты языков C и C++:

  1. указывают несколько допустимых поведений из числа возможных (см. неуточняемое поведение );
  2. указывают единственно допустимое поведение из числа возможных либо
  3. явно указывают, что поведение не определено (см. неопределённое поведение ).

Пример 1. Неуточняемое поведение.

g() + f()

Оператор « + » не является точкой следования, поэтому неизвестно, какая из функций будет вызвана первой: f () или g () . Поведение зависит от реализации компилятора .

Пример 2. Единственно допустимое поведение.

f(), g()

Оператор « , » является точкой следования, поэтому порядок вычисления гарантируется стандартом и известен заранее (слева направо):

  • сначала вычисляется левый операнд: вызывается функция f () ;
  • затем — правый: вызывается функция g () .

Пример 3. Неопределённое поведение.

i = ++i + i++

С точки зрения языка C указанное выражение содержит множественные модификации переменной i , не упорядоченные относительно друг друга. Поведение данного выражения не определено. (В то же время с точки зрения современного языка C++, который существенно более строго упорядочивает процесс вычисления оператора присваивания, поведение этого выражения полностью определено.)

Точки следования в языках C и C++

В оригинальных стандартах языков C и C++ были определены следующие точки следования:

  • точки следования для операторов « && », « || » и « , ». Эти операторы гарантированно вычисляются слева направо, если не перегружены. Пример. В выражении « * p ++ != 0 && * q ++ != 0 » сначала вычисляется левый операнд (« * p ++ != 0 »); результат приводится к типу bool и сравнивается с true ; если равен true , вычисляется правый операнд (« * q ++ != 0 »), иначе возвращается false ;
  • точка следования для тернарного оператора « ?: ». 1-й операнд вычисляется первым; затем располагается точка следования; 2-й операнд вычисляется только, если 1-й операнд равен true ; 3-й операнд вычисляется только, если 1-й операнд равен false . Пример. В выражении « a == ( * p ++ ) ? ( * p ++ ) : 0 » сначала выполняется 1-й операнд (« * p ++ »; переменная p увеличивается на 1 ); результат вычисления приводится к типу bool и сравнивается с true ; если равен true , выполняется 2-й операнд (« ( * p ++ ) »), иначе — 3-й (« 0 »);
  • точки следования в выражениях:
    • на месте символа « ; » в выражениях, являющихся отдельными инструкциями. Например, в выражении « a = b ; » точка следования вставляется вместо « ; »;
    • в конце выражения, записанного после ключевого слова return ; а точнее, на момент, когда возвращаемое значение будет скопировано в контекст вызывающей функции. Эта точка следования явно описана только в стандарте C++;
    • в конце выражений, записанных в круглых скобках после ключевых слов if , switch , while (включая while в конструкции do-while );
    • в концах каждого из трёх выражений для цикла for ;
  • перед вызовом функции. Порядок вычисления аргументов функции не определён. Точка следования гарантирует, что все аргументы будут вычислены до вызова функции. Пример. Рассмотрим выражение « f ( i ++ ) + g ( j ++ ) + h ( k ++ ) ». Сначала создаётся временная переменная со значением, равным значению переменной i ; затем для переменной i (не для временной) вызывается оператор «постфиксный ++»; наконец, вызывается функция f () с временной переменной в качестве аргумента. Сказанное справедливо для переменных j , k и функций g () , h () соответственно. При этом из-за отсутствия точки следования у оператора «+» порядок вызова функций f () , g () и h () не определён. Следовательно не определён и порядок вызова операторов «постфиксный ++» для переменных i , j и k . То есть при выполнении функции f () неизвестно, были ли вызваны операторы «постфиксный ++» для переменных j и k . Пример. Рассмотрим выражение « f ( a , b , c ) ». Запятая между аргументами функции не является оператором «запятая» и не гарантирует порядок вычисления значений аргументов. Порядок вычисления значений аргументов функции не стандартизован и зависит от реализации компилятора;
  • в объявлении с инициализацией на момент завершения вычисления инициализирующего значения. Пример. Рассмотрим выражение « int a = ( 1 + i ++ ); ». Точка следования вставляется после вычисления выражения « ( 1 + i ++ ) »;
  • перед вызовом перегруженного оператора в языке C++. Точка следования гарантирует вычисление значений аргументов оператора (как и обычной функции) до его вызова.

См. также

Примечания

Ссылки

  • cppreference.com. Порядок вычислений в (англ.) и (англ.)
Источник —

Same as Точка следования