Является ли эта операция правильно упорядоченной?
В следующем фрагменте кода из более обширного фрагмента кода, представленного
void func(int* usedNum, int wher) {
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
}
int main(void) {
int a = 11, b = 2;
func(&a, b);
}
предупреждение издается
warning: operation on '* usedNum' may be undefined [-Wsequence-point]
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
Есть ли проблема с кодом?
Мой источник сомнений был этот и часть, где говорится
Точки последовательности в логических выражениях, таких как && и || и тернарный оператор?:, а оператор запятой означает, что операнд левой стороны оценивается перед операндом правой стороны. Эти несколько операндов являются единственными операндами в С++, которые вводят точки последовательности.
TL;DR
Для тех, кто находит пытки, чтобы читать комментарии: первоначальный вопрос не был правильно поставлен, и было бы несправедливо создавать неправильные представления. Мой взгляд на эту тему имел две стороны
-
Тернарный оператор не испортит (неожиданным образом) точки последовательности (что выполняется, две ветки секвенированы в каждой версии C, С++ - см. предоставленную ссылку)
-
Является ли x = ++x
проблемой? Как видно из ссылки coliru, мы компилируем для С++ 14. Там операция хорошо определена (ссылки на комментарии), но более старые версии С++ и c рассматривают это как undefined. Так почему же существует предупреждение?
Ответы фокусируются как на C, так и на С++; это - хорошая ссылка. Наконец, тег C был первоначально (мой плохой) и не может быть удален, потому что существующие вышеперечисленные ответы относятся к нему
Ответы
Ответ 1
Когда условие истинно, это эквивалентно выражению x = ++x
. В C и версиях С++ до С++ 11 это представляет собой модификацию и чтение x
без промежуточной точки последовательности и, следовательно, поведение undefined, если соблюдается истинная ветвь. Начиная с С++ 11, x = ++x
упорядочен и четко определен.
Изменить. Чтобы прояснить некоторые проблемы из комментариев.
1) это будет хорошо определено во всех стандартах C и С++:
x = (++x, x); // RHS evaluates to x after increment
потому что выражение в круглых скобках включает в себя оператор запятой, который вводит точку последовательности между оценкой его операндов. Таким образом, все выражение в RHS оценивается до x
после приращения. Но код в вашем вопросе не связан с оператором запятой.
2) Тернарный оператор вводит точку последовательности
Это точка последовательности между условием и двумя ветвями. Но это не указывает точку последовательности между веткой и присваиванием.
Ответ 2
Предупреждение, которое вы получаете, вероятно, связано с тем, что вы компилируете свой код в режиме С++ 03 или старше. В C99 и С++ 03 выражение
x = ++x;
вызывает поведение undefined. Причина в том, что между двумя точками последовательности объект не может изменять более одного раза.
Это правило изменяется в C11 и С++ 11. Согласно C11, правило выглядит следующим образом:
C11: 6.5 Выражения:
Если побочный эффект на скалярном объекте непересекающийся по отношению к другому побочному эффекту на том же скалярном объекте или вычислении значения с использованием значения одного и того же скалярного объекта, поведение undefined.
Когда *usedNum + 1 > wher
будет true
, тогда
*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;
будет эквивалентно
*usedNum = ++(*usedNum);
и согласно новому правилу это хорошо определено в С++ 11, это связано с тем, что побочный эффект pre ++
секвенирован перед побочным эффектом оператором =
. Подробнее читайте этот ответ.
Но одно и то же выражение *usedNum = ++(*usedNum);
вызывает undefined поведение в C11. Причина в том, что нет гарантии того, что побочный эффект оператора =
секвенируется после побочного эффекта оператора pre ++
.
Примечание: В выражении
a = x++ ? x++ : 0;
есть точка последовательности после первого x++
, и, следовательно, поведение хорошо определено. То же самое верно для
x = (++x, x);
потому что существует точка последовательности между оценкой левого и правого операнда и, следовательно, побочный эффект секвенирован.