Разница между C и C++ относительно оператора ++


я дурачился с каким-то кодом и увидел что-то, что я не понимаю "почему".

int i = 6;
int j;

int *ptr = &i;
int *ptr1 = &j

j = i++;

//now j == 6 and i == 7. Straightforward.

Что делать, если вы поместите оператор на левой стороне знака равенства?

++ptr = ptr1;

эквивалентно

(ptr = ptr + 1) = ptr1; 

, тогда как

ptr++ = ptr1;

эквивалентно

ptr = ptr + 1 = ptr1;

постфикс запускает ошибку компиляции, и я ее получаю. У вас есть константа "ptr + 1" в левой части оператора присваивания. Справедливо.

префикс один компилируется и работает в C++. Да, я понимаю, что это грязно, и Вы имеете дело с нераспределенной памятью, но она работает и компилируется. В C это не компилируется, возвращая ту же ошибку, что и постфикс "lvalue required as left operand of assignment". Это происходит независимо от того, как он написан, расширен с помощью двух операторов "=" или с синтаксисом "++ptr".

в чем разница между тем, как C обрабатывает такое назначение и как C++ обрабатывает его?

2 70

2 ответа:

в обоих C и C++, результат x++ является rvalue, поэтому вы не можете назначить его.

В C,++x эквивалентно x += 1 (C standard §6.5.3.1 / p2; все c standard cites относятся к WG14 N1570). В C++, ++x эквивалентно x += 1 Если x - это не bool (стандарт C++ §5.3.2 [expr.пред.incr] / p1; все стандартные цитаты C++ относятся к WG21 N3936).

в C результатом выражения присваивания является значение rvalue (стандарт C §6.5.16 / p3):

оператор присваивания сохраняет значение в объект, указанный левый операнд. Выражение присваивания имеет значение левого операнд после выполнения задания, но не является левосторонним значением.

потому что это не lvalue, вы не можете назначить ему: (c standard §6.5.16/p2 - обратите внимание, что это ограничение)

оператор присваивания должен иметь изменяемое значение lvalue слева операнд.

в C++ результатом выражения присваивания является значение lvalue (стандарт C++ §5.17 [expr.ass]/p1):

оператор присваивания (=) и составные операторы присваивания все группа справа налево. Все требуют изменяемые значения, как их оставили операнд и возвращает значение lvalue, относящееся к левому операнду.

так ++ptr = ptr1; является диагностируемым нарушением ограничений в C, но не нарушает никаких диагностируемых правил в С.++

однако, pre-C++11,++ptr = ptr1; имеет неопределенное поведение, так как он изменяет ptr в два раза между двумя соседними точками последовательности.

в C++11, поведение ++ptr = ptr1 будет четко определена. Это яснее, если мы перепишем его как

(ptr += 1) = ptr1;

начиная с C++11, стандарт C++ предусматривает, что (§5.17 [expr.ass]/p1)

во всех случаях присвоение выполняется последовательно после вычисления значения правого и левого операндов, а также до расчет стоимости выражение присваивания. В отношении Ан неопределенно-последовательный вызов функции, операция соединения назначение оценки.

Итак задание выполнено = применяется после вычисления значения ptr += 1 и ptr1. Задание, выполняемое += секвенируется перед вычислением значения ptr += 1, и все вычисления значения, необходимые для += обязательно секвенированный перед этим заданием. Таким образом, последовательность здесь четко определена и нет неопределенного поведения.

в C результатом приращения pre и post являются rvalues, и мы не можем назначить rvalue нам нужен lvalue(Также см.: понимание lvalues и rvalues в C и c++). Мы можем видеть, перейдя к проект стандарта C11 раздел 6.5.2.4Постфиксные операторы инкремента и декремента где сказано (акцент мой идет вперед):

в результат оператора postfix ++значение из операнд. [...] См. обсуждение аддитивных операторов и соединений назначение информации об ограничениях, типах и преобразованиях и влияние операций на указатели. [...]

таким образом, результат пост-инкремент-это стоимостью что является синонимом rvalue и мы можем подтвердить это, зайдя в раздел 6.5.16назначение операторы который пункт выше указывает нам для дальнейшего понимания ограничений и результатов, он говорит:

[...] Выражение присваивания имеет значение левого операнда после задание,но это не lvalue.[...]

что еще раз подтверждает, что результат постинкремента не является lvalue.

для предварительного приращения мы можем видеть из раздела 6.5.3.1инкремент префиксный и операторы декремента он говорит:

[...]См. обсуждение аддитивных операторов и составного назначения для информация об ограничениях, типах, побочных эффектах и преобразованиях и влияние операций на указатели.

также указывает на 6.5.16 как и постинкремент, и поэтому результат предварительного инкремента в C также не является lvalue.

в C++ пост-инкремент также является rvalue, точнее a prvalue мы можем подтвердить это, зайдя в раздел 5.2.6инкремент и декремент он говорит:

[...]результат является prvalue. тип результата является резюме-неквалифицированный версия типа операнда[...]

в отношении предварительного приращения C и C++ отличаются. В C результатом является rvalue в то время как в C++ результат a lvalue это объясняет, почему ++ptr = ptr1; работает на C++ , но не C.

для C++ это описано в разделе 5.3.2инкремент и декремент он говорит:

[...]Результатом является обновленный операнд;это lvalue, и это битовое поле, если операнд является битовым полем.[...]

чтобы понять:

++ptr = ptr1;

хорошо определен или нет в C++ нам нужны два разных подходит один для pre C++11 и один для C++11.

Pre C++11 это выражение вызывает неопределенное поведение, так как он изменяет объект более одного раза в одной и той же точке последовательности. Мы можем увидеть это, перейдя в предварительный C++11 проект стандартного раздела 5выражения он говорит:

за исключением случаев, когда отмечено, порядок оценки операндов отдельных операторы и подвыражения отдельных выражений, а также порядок при каких именно побочных эффектах имеют место, не уточняется.57) между предыдущая и следующая точки последовательности скалярный объект должен быть сохранен значение изменено не более одного раза при вычислении выражения. Кроме того, предварительное значение должно быть доступно только для определения значение для хранения. Требования настоящего пункта должны быть выполнены для каждого допустимого порядка подвыражений полного выражение; в противном случае поведение не определено. [ Пример:

 i = v[i ++]; / / the behavior is undefined
 i = 7 , i++ , i ++; / / i becomes 9
 i = ++ i + 1; / / the behavior is undefined
 i = i + 1; / / the value of i is incremented

-конец примера ]

мы увеличиваем ptr а затем последовательно присваивать ему, что является двумя модификациями и в этом случае точка последовательности происходит в конце выражения после ;.

для C+11, мы должны пойти в отчет о дефекте 637: правила последовательности и пример не согласны который был отчет о дефекте, который привел к:

i = ++i + 1;

становится четко определенным поведение в C++11 тогда как до C++11 это было неопределенное поведение. Объяснение в этом докладе является одним из лучших, которые я даже видел, и чтение его много раз было поучительным и помогло мне понять многие концепции в Новом Свете.

логика, которая приводит к тому, что это выражение становится хорошо определенным поведением, выглядит следующим образом:

  1. побочный эффект присваивания должен быть упорядочен после вычислений значений обоих его LHS и RHS (5.17 [expr.ass] пункт 1).

  2. LHS (i) является lvalue, поэтому его вычисление значения включает вычисление адреса i.

  3. чтобы вычислить значение RHS (++i + 1), необходимо сначала вычислить значение выражения lvalue ++i, а затем выполнить преобразование lvalue-rvalue в результат. Это гарантирует, что побочный эффект приращения секвенируется перед вычислением операции сложения, которая в поворот секвенируется перед назначением побочного эффекта. Другими словами, он дает четко определенный порядок и конечное значение для этого выражения.

логика несколько схожа для:

++ptr = ptr1;
  1. вычисления значения LHS и RHS секвенируются перед побочным эффектом назначения.

  2. RHS является значением lvalue, поэтому его вычисление значения включает вычисление адреса ptr1 выглядит следующим образом.

  3. для того, чтобы стоимость рассчитать ЛХС (++PTR) и, надо первое значение-вычисление значения выражения ++PTR и затем делать это значение lvalue-на-правосторонним значением преобразования на результат. Это гарантирует, что побочный эффект приращения будет упорядочен перед побочным эффектом назначения. Другими словами, он дает четко определенный порядок и конечное значение для этого выражения.

Примечание

ФП сказал:

Да, я понимаю, что это грязно, и Вы имеете дело с нераспределенными память, но она работает и компилируется.

указатели на объекты без массива считаются массивами размера один для аддитивных операторов, я собираюсь процитировать проект стандарта C++, но C11 имеет почти тот же самый текст. Из раздела 5.7аддитивные операторы:

для целей настоящих операторов, указатель на немассива объект ведет себя так же, как указатель на первый элемент массива длина один с типом объекта в качестве его типа элемента.

и далее говорит нам, что указание одного за конец массива допустимо до тех пор, пока вы не разыменуете указатель:

[...]Если и операнд указателя, и результат указывают на элементы тот же объект массива,или один после последнего элемента массива объект оценка должна не производить переполнение; в противном случае поведение не определено.

так:

++ptr ;

по-прежнему является допустимым указателем.