В C и C++ - выражение, использующее оператор запятой, такой как "a = b, ++a"; не определено?
Возьмите эти три фрагмента кода C:
1) a = b + a++
2) a = b + a; a++
3) a = b + a, a++
Всем известно, что пример 1 - очень плохая вещь и явно вызывает неопределенное поведение. Пример 2 не имеет проблем. Мой вопрос касается примера 3. Работает ли запятый оператор как точка с запятой в этом выражении? Являются ли 2 и 3 эквивалентными или равно 3 так же неопределенными, как 1?
В частности, я рассматривал это как нечто вроде free(foo), foo = bar
. Это в основном та же проблема, что и выше. Могу ли я быть уверенным, что foo освобождается до его переназначения или это проблема четкой последовательности?
Я знаю, что оба примера в основном бессмысленны, и имеет смысл использовать точку с запятой и делать с ней. Я просто спрашиваю из любопытства.
Ответы
Ответ 1
Случай 3 четко определен.
Раздел 6.5.17 в стандарте C относительно оператора запятой ,
говорит следующее:
2 Левый операнд оператора запятой оценивается как выражение в виде пустоты; между его оценкой и точкой правильного операнда есть точка последовательности. Затем оценивается правый операнд; результат имеет свой тип и значение
Раздел 5.14 p1 стандарта С++ 11 имеет аналогичный язык:
Пара выражений, разделенных запятой, оценивается слева направо; левое выражение представляет собой выражение discarded-. Каждое вычисление значения и побочный эффект, связанные с левым выражением, секвенируются перед вычислением каждого значения и побочным эффектом, связанным с правильным выражением. Тип и значение результата - это тип и значение правильного операнда; результат имеет такую же категорию значений, что и его правый операнд, и является битовым полем, если его правый операнд - это значение glvalue и бит-поле.
Из-за точки последовательности a = b + a
гарантируется, что будет полностью оценена до a++
в выражении a = b + a, a++
.
Что касается free(foo), foo = bar
, это также гарантирует, что foo
свободен до назначения нового значения.
Ответ 2
a = b + a, a++;
хорошо определен, но a = (b + a, a++);
может быть неопределенным.
Прежде всего, приоритет оператора делает выражение эквивалентным (a = (b+a)), a++;
, Где +
имеет наивысший приоритет, а затем =
, а затем ,
. Оператор запятой включает в себя точку последовательности между оценкой ее левого и правого операндов. Таким образом, код, неинтересно, полностью эквивалентен:
a = b + a;
a++;
Что, конечно, четко определено.
Если бы мы вместо этого писали a = (b + a, a++);
, то точка последовательности в операторе запятой не спасет день. Потому что тогда выражение было бы эквивалентно
(void)(b + a);
a = a++;
- В C и C++ 14 или старше
a = a++
имеет последствий (см. C11 6.5.16/3). Это означает, что это неопределенное поведение (Per C11 6.5/2). Обратите внимание, что C++ 11 и C++ 14 были плохо сформулированы и неоднозначны. - В C++ 17 или позже операнды оператора
=
упорядочены справа налево, и это все еще четко определено.
Все это предполагает, что перегрузка оператора C++ не происходит. В этом случае параметры для перегруженной операторной функции будут оцениваться, точка последовательности имеет место до вызова функции, а то, что происходит от нее, зависит от внутренних функций этой функции.