В C результат до и после приращения является rvalues и мы не можем назначить к RValue, нужно именующее выражение (also see: Understanding lvalues and rvalues in C and C++). Мы можем увидеть, перейдя в раздел draft C11 standard6.5.2.4
Postfix инкремента и декремента операторы который говорит (курсив мой идти вперед):
The result of the postfix ++ operator is the value of the operand. [...] See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. [...]
Таким образом, результатом постинкремента является значение которое является синонимом для rvalue, и мы можем это подтвердить, перейдя в раздел 6.5.16
Операторы присваивания, которые вышеприведенный параграф указывает нам на дальнейшее понимание ограничений и результатов, в нем говорится:
[...] An assignment expression has the value of the left operand after the assignment, but is not an lvalue.[...]
, который далее подтверждает результат послеинструмента, не является lvalue.
Для преинкремента мы можем видеть из раздела 6.5.3.1
префиксов увеличивающие и уменьшающие операторов, который говорит:
[...]See the discussions of additive operators and compound assignment for information on constraints, types, side effects, and conversions and the effects of operations on pointers.
также указывает обратно на 6.5.16
как постинкремента делает и поэтому результат преинкремента в C также не является lvalue.
В C++ пост-инкремент также Rvalue, более конкретно prvalue мы можем подтвердить это, перейдя в раздел 5.2.6
инкремента и декремента, который говорит:
[...]The result is a prvalue. The type of the result is the cv-unqualified version of the type of the operand[...]
Что касается предварительного -инкремент C и C++ различаются. В C результат равен rvalue, тогда как в C++ результатом является lvalue, что объясняет, почему ++ptr = ptr1;
работает на C++, но не C.
Для C++ это рассматривается в разделе 5.3.2
инкремента и декремента, который говорит:
[...]The result is the updated operand; it is an lvalue, and it is a bit-field if the operand is a bit-field.[...]
Чтобы понять, является ли:
++ptr = ptr1;
хорошо определен или не в C++ нам нужны два различных подхода один для pre C++ 11 и один для C++ 11.
Pre C++ 11 это выражение вызывает undefined behavior, так как он изменяет объект более одного раза в одной и той же точке последовательности. Мы можем увидеть, перейдя на заранее C++ 11 проекта стандарта разделе 5
выражений, который говорит:
Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.57) Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined. [ Example:
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
—end example ]
Мы увеличивающимся ptr
, а затем впоследствии назначая ему, что две модификации, и в этом случае точка последовательности происходит в конце выражения после ;
.
Для C + 11, мы должны идти к defect report 637: Sequencing rules and example disagree который был отчет дефект, в результате:
i = ++i + 1;
становится хорошо определено поведение в C++ 11, тогда как до C++ 11 это undefined behavior. Объяснение в этом отчете является одним из лучших, которые я даже видел, и его много раз читал, и он помог мне понять многие концепции в новом свете.
Логика, которая приведет к этому выражению становится хорошо определены поведение выглядит следующим образом:
Назначение побочного эффекта требуется, чтобы быть секвенированы после значения вычислений как его LHS и RHS (5.17 [выражение .ass] пункт 1).
LHS (i) является lvalue, поэтому его вычисление значения включает в себя вычисление адреса i.
Для вычисления стоимости RHS (++ i + 1) необходимо сначала вычислить выражение lvalue ++ i, а затем выполнить преобразование lvalue-to-rval в результате. Это гарантирует, что побочный эффект приращения секвенирован перед вычислением операции сложения, которая, в свою очередь, секвенируется перед побочным эффектом присваивания. Другими словами, он дает четко определенный порядок и конечное значение для этого выражения.
Логика несколько похож на:
++ptr = ptr1;
В стоимости вычисления в LHS и RHS секвенирует до назначения побочного эффекта.
RHS - это lvalue, поэтому его вычисление значения включает в себя вычисление адреса ptr1.
Для вычисления стоимости LHS (++ ptr) необходимо сначала вычислить выражение lvalue ++ ptr, а затем выполнить преобразование lvalue-to-rval в результате.Это гарантирует, что побочный эффект приращения секвенирован перед побочным эффектом присваивания. Другими словами, он дает четко определенный порядок и конечное значение для этого выражения.
Примечание
ОП сказал:
Yes, I understand it's messy and you're dealing with unallocated memory, but it works and compiles.
указатели на объекты, не массив считаются массивы размером один для аддитивных операторов, я собираюсь процитировать проект C++ стандарт но C11 имеет почти тот же текст. Из раздела 5.7
Аддитивные операторы:
For the purposes of these operators, a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.
и далее говорит нам, указывая один мимо конца массива является действительным до тех пор, пока вы не разыменования указателя:
[...]If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
так:
++ptr ;
по-прежнему действительный указатель.
Насколько я знаю, '++ i' не возвращает l-значение в C. Независимо, это UB, когда вы изменяете переменную 2 раза между двумя последовательными точками последовательности. Другими словами, неуказано, увеличивается ли значение сначала или оно назначается первым. – bolov
@OllieFord Я никогда не понимал, что UB означает, что он не может компилироваться. Я имею в виду, люди говорят, что все может случиться, но я считаю, что все может случиться * при запуске кода *. – juanchopanza
@juanchopanza код руны, это UB, поэтому программа возвращается во времени и останавливает процесс компиляции. Итак ... да ... – bolov