Автобус дальнего следования
- 1 year ago
- 0
- 0
Точка следования ( англ. 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++:
Пример 1. Неуточняемое поведение.
g() + f()
Оператор «
+
» не является точкой следования, поэтому неизвестно, какая из функций будет вызвана первой:
f
()
или
g
()
. Поведение зависит от реализации
компилятора
.
Пример 2. Единственно допустимое поведение.
f(), g()
Оператор «
,
» является точкой следования, поэтому порядок вычисления гарантируется стандартом и известен заранее (слева направо):
f
()
;
g
()
.
Пример 3. Неопределённое поведение.
i = ++i + i++
С точки зрения языка C указанное выражение содержит множественные модификации переменной
i
, не упорядоченные относительно друг друга. Поведение данного выражения не определено. (В то же время с точки зрения современного языка C++, который существенно более строго упорядочивает процесс вычисления оператора присваивания, поведение этого выражения полностью определено.)
В оригинальных стандартах языков C и C++ были определены следующие точки следования:
*
p
++
!=
0
&&
*
q
++
!=
0
» сначала вычисляется левый операнд («
*
p
++
!=
0
»); результат приводится к типу
bool
и сравнивается с
true
; если равен
true
, вычисляется правый операнд («
*
q
++
!=
0
»), иначе возвращается
false
;
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
++
)
»;