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

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

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

Побочные эффекты (англ. side effects) — любые действия работающей программы, изменяющие среду выполнения (англ. execution environment)[1]. Например, к побочным эффектам относятся[2]:

  • доступ (чтение или запись) к объекту, определённому с модификатором volatile (англ.);
  • изменение (запись) объекта;
  • изменение файла;
  • изменение поведения инструкций процессора, обрабатывающих числа с плавающей точкой (см. floating-point environment (англ.));
  • вызов функции, выполняющей перечисленные выше действия.

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

Примеры неоднозначности в языках C и C++[править | править вики-текст]

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

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

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

g() + f()

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

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

f(), g()

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

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

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

i = i++

Указанное выражение не содержит ни одной точки следования, поэтому компилятор не может определить порядок вычисления операторов, соответствующий стандартам. Поведение зависит от реализации компилятора.

Точки следования в языках 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; а точнее, на момент, когда возвращаемое значение будет скопировано в контекст вызывающей функции. Эта точка следования явно описана только в стандарте С++;
    • в конце выражений, записанных в круглых скобках после ключевых слов 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++. Точка следования гарантирует вычисление значений аргументов оператора (как и обычной функции) до его вызова.

См. также[править | править вики-текст]

Ссылки[править | править вики-текст]

Примечания[править | править вики-текст]

  1. International standard. ISO/IEC 9899:201x. Information technology — Programming languages — C Committee draft N1570 (англ.) (pdf) Пункт 5.1.2.3, параграф 2. ISO/IEC (April 12, 2011). — Черновик стандарта C11. Проверено 22 ноября 2014.
  2. cppreference.com. «Порядок вычислений» (англ.).