В 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++ не происходит. В этом случае параметры для перегруженной операторной функции будут оцениваться, точка последовательности имеет место до вызова функции, а то, что происходит от нее, зависит от внутренних функций этой функции.