Вот пример
#include <iostream>
using namespace std;
int main()
{
int x = 0;
cout << (x == 0 ? x++ : x) << endl; //operator in branch
cout << "x=" << x << endl;
cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
cout << "x=" << x << endl;
return 0;
}
выход:
0
x=1
1
x=1
Я понимаю вывод, но это неопределенное поведение или нет? Гарантирован ли порядок оценки в любом случае?
Даже если это гарантировано, я прекрасно понимаю, что увеличение / уменьшение может быстро стать проблемой для удобства чтения. Я только спрашиваю, поскольку я видел подобный код и был сразу же не уверен, учитывая, что есть много примеров неоднозначного / неопределенного использования операторов увеличения / уменьшения, таких как …
C ++ не определяет порядок, в котором оцениваются параметры функции. ↪
int nValue = Add(x, ++x);
Язык C ++ говорит, что вы не можете изменять переменную более одного раза между точками последовательности. ↪
x = ++y + y++
Поскольку операторы увеличения и уменьшения имеют побочные эффекты, использование выражений с операторами увеличения или уменьшения в макросе препроцессора может привести к нежелательным результатам. ↪
#define max(a,b) ((a)<(b))?(b):(a)
k = max( ++i, j );
Для условного оператора (§5.16 [expr.cond] / p1):
Каждое значение вычисления и побочный эффект, связанный с первым
Выражение секвенируется перед каждым вычислением значения и побочным эффектом.
связано со вторым или третьим выражением.
Для логического оператора ИЛИ (§5.15 [expr.log.or] / p1-2):
второй операнд не оценивается, если первый операнд оценивается как
true
,
[…] Если вычисляется второе выражение, каждое вычисление значения и
побочный эффект, связанный с первым выражением
каждое значение вычисления и побочный эффект, связанный со вторым
выражение.
Поведение вашего кода четко определено.
Существует гарантированный порядок выполнения в троичных операторах и логических &&
а также ||
операции, поэтому нет конфликта в точках последовательности оценки.
Один за раз
cout << (x == 0 ? x++ : x) << endl; //operator in branch
Будет всегда выводить x
но будет увеличивать его, только если это было 0.
cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
Это тоже хорошо определено, если x
было 1, оно не будет оценивать RHS, если не было, то уменьшит его, но --x
никогда не будет 0, поэтому оно будет истинным, если x == 1, и в этом случае x
также теперь будет 0.
В последнем случае, если х INT_MIN
это не вполне определенное поведение, чтобы уменьшать его (и он будет выполняться).
Это не может произойти в первом случае, когда х не будет 0, если это INT_MAX
так что вы в безопасности.
Я понимаю вывод, но это неопределенное поведение или нет?
Код отлично определен. Стандарт C11 гласит:
Первый операнд оценивается; между его оценкой и
оценка второго или третьего операнда (что бы ни оценивалось). Второй операнд
оценивается, только если первое сравнение не равно0
; третий операнд вычисляется только если
первое сравнение равно0
; результатом является значение второго или третьего операнда
(в зависимости от того, что оценивается), преобразуется в тип, описанный ниже. 110)
В отличие от побитового
|
оператор,||
оператор гарантирует оценку слева направо; если
второй операнд оценивается, между оценками первого
и вторые операнды. Если первый операнд сравнивается неравно0
второй операнд
Не Оценено.
В дальнейшем вики объясняет это примером:
Между оценкой левого и правого операндов
&&
(логическое И),||
(логическое ИЛИ) (как часть оценки короткого замыкания), иcomma operators
, Например, в выражении*p++ != 0 && *q++ != 0
, все побочные эффекты под-выражения*p++ != 0
завершены до любой попытки доступаq
,Между оценкой первого операнда троичного оператора «вопросительный знак» и второго или третьего операнда. Например, в выражении
a = (*p++) ? (*p++) : 0
после первого есть точка последовательности*p++
Это означает, что он уже был увеличен ко времени выполнения второго экземпляра.
Правило для ||
а также ?:
то же самое для C ++ (раздел 5.15 и 5.16) как в C.
Гарантирован ли порядок оценки в любом случае?
Да. Порядок оценки операндов операторов ||
, &&
, ,
а также ?:
гарантированно будет слева направо.
В C сохраненное значение объекта может быть изменено только один раз между двумя точками последовательности.
Точка последовательности происходит:
&&
, ||
а также ?:
операторыТак, например, это выражение x = i++ * i++
является не определено, в то время как x = i++ && i++
является совершенно законно.
Ваш код показывает определенное поведение.
int x = 0;
соиЬ << (х == 0? х ++: х) << епсИ;
В приведенном выше выражении x
является 0
, так x++
будет выполнено, здесь x ++ — постинкремент, поэтому он выведет 0
,
соиЬ << «Х =» << Икс << епсИ;
В приведенном выше выражении как x
теперь имеет значение 1
поэтому вывод будет 1
,
соиЬ << (x == 1 || —x == 0? 1: 2) << епсИ;
Вот x
является 1
поэтому следующее условие не оценивается (--x == 0
) и на выходе будет 1
,
соиЬ << «Х =» << Икс << епсИ;
Как выражение --x == 0
не оценивается, выход снова будет 1
,
Да, безопасно использовать операторы увеличения / уменьшения, как у вас. Вот что происходит в вашем коде:
cout << (x == 0 ? x++ : x) << endl; //operator in branch
В этом фрагменте вы тестируете x == 0
, который true
, Так как это true
Ваше троичное выражение оценивает x++
,
Так как вы используете пост-приращение здесь, исходное значение для x
выводится на стандартный поток вывода, а потом x
увеличивается
cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
Этот фрагмент немного более запутанный, но все же дает предсказуемый результат. С этой точки зрения, x = 1
из первого фрагмента. В троичном выражении условная часть вычисляется первой; однако из-за Короткое замыкание, второе условие, --x == 0
, никогда не оценивается.
Для C ++ операторы ||
а также &&
являются короткозамкнутыми логическими операторами для логическое ИЛИ а также логическое И соответственно. Когда вы используете эти операторы, ваши условия проверяются (слева направо) до определения окончательного результата. Как только результат определен, больше никаких условий не проверяется.
Глядя на фрагмент # 2, ваше первое условие проверяет, x == 1
, Поскольку ваше первое условие оценивается как true
и вы используете логическое ИЛИ, нет необходимости продолжать оценивать другие условия. Это означает, что --x == 0
является никогда не казнил.
Короткое замыкание полезно для повышения производительности вашей программы.
Предположим, у вас было такое условие, которое вызывает несколько дорогостоящих функций:
if (task1() && task2())
{
//...Do something...
}
В этом примере task2
никогда не должен называться, если task1
завершается успешно (task2
зависит от некоторых данных, которые изменяются task1
).
Потому что мы используем оператор короткого замыкания И, если task1
не возвращается false
, тогда оператор if обладает достаточной информацией, чтобы досрочно завершить работу и прекратить проверку других условий. Это означает, что task2
никогда не называется.