在现代C++(至少是C++2011年及以后)中,两者都不是未界定的行为。 即便不是,实施的定义是或unspecised。 (所有三个术语都是不同的。)
These two lines are both well defined (but they do different things).
When you have pointers p1 以及 p2 to scalar types then
*++p1 = *p2++;
等于
p1 = p1 + 1;
*p1 = *p2;
p2 = p2 + 1;
(^^^this is also true for C++ 1998/2003)
以及
++*p1 = *p2++;
等于
*p1 = *p1 + 1;
*p1 = *p2;
p2 = p2 + 1;
(C++ 1998/2003年) 或不可(如下文所述)
Obviously in case 2 incrementing value 以及 then assigning to it (thus overwriting just incremented value) is pointless - but there may be similar examples that make sense (e.g. +=
instead of =
).
BUT like many people point out - just don t write the code that looks ambiguous or unreasonably complex. Write the code that is clear to you 以及 supposed to be clear to the readers.
Old C++ 1998/2003 第二种表述是一个奇怪的问题:
在阅读预先加固操作员的说明之后,首先:
<>strongISO>IEC 14882-2003 5.3.2:
- The oper以及 of prefix ++ is modified by adding 1, or set to true if it
is bool (this use is deprecated). The oper以及 shall be a modifiable
lvalue. The type of the oper以及 shall be an arithmetic type or a
pointer to a completely-defined object type. The value is the new
value of the oper以及; it is an lvalue. If x is not of type bool, the
expression ++x 等于 x+=1.
I personally have a strong feeling that everything is perfectly defined 以及 obvious 以及 the same as above for C++ 2011 以及 later.
At least in the sense that every reasonable C++ implementation will behave in exact same well defined way (including old ones).
Why it should be otherwise if we always intuitively rely on a general rule that in any simple operator evaluation within a complex expression we evaluate its oper以及s first 以及 after that apply the operator to the values of those oper以及s. Right? Breaking this intuitive expectation would be extremely stupid for any programming language.
So for the full expression ++*p1 = *p2++;
we have oper以及s: 1 - ++*p1
evaluated as already incremented lvalue (as defined in the above quote from C++ 2003) 以及 2 - *p2++
that is an rvalue stored at pointer p2
before its increment. It doesn t look ambiguous at all. Of course in this case - no reason to increment a value you are overwriting anyway BUT if there was double increment instead - ++(++*p1);
OR other kind of assignment like +=
/-=
/&=
/*=
/etc instead of simple assignment THAT would not be unreasonable at all.
Unfortunately all the intuition 以及 logic is messed up by this:
<>strongISO>IEC 14882-2003 - 5 expression:
- Except where noted, the order of evaluation of oper以及s
of individual operators 以及 subexpressions of individual
expressions, 以及 the order in which side effects
take place, is unspecified. Between the previous
以及 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 unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
—end example]
So this wording if interpreted in a paranoid way seems to imply that modification of a value stored in a specific location more than once without intervening sequence point is explicitly forbidden by this rule 以及 the last sentence declares that failing to comply with every requirement is Undefined Behavior. AND our expression seems to modify the same location more that once (?) with no sequence point until the full expression evaluated. (This arbitrary 以及 unreasonable limitation is reinforced further by example 3 - i = ++i + 1;
though it says // the behavior is unspecified
- not undefined as in the wording before - which only adds more confusion.)
BUT on the other h以及... If we ignore the example 3. (Maybe i = ++i + 1;
is a typo 以及 there should have been postfix increment instead - i = i++ + 1;
? Who knows... Anyway examples are not part of formal specification.) If we interpret this wording in the most permissive way - we can see that in each allowed order of evaluation of subexpressions of the whole expression - preincrement ++*p1
must be evaluated to an LVALUE (which is something that allows further modification) BEFORE applying assignment operator so the only valid final value at that location is the one that is stored with assignment operator. ALSO NOTE that conforming C++ implementation have no obligation to actually modify that location more than once 以及 may instead store only final result - that is both reasonable optimization allowed by the st以及ard 以及 may be actual dem以及 of this article.
Which one of those interpretations is correct? Paranoid or permissive? Universally applicable logic or some suspicious 以及 ambiguous words in a document almost nobody really ever read? Blue pill or Red pill?
谁知道...... 它看起来像一个灰色区域,需要作不明确的解释。
If we interpret the quote from C++ 2003 st以及ard above in a paranoid way then it looks like this code may be Undefined Behavior:
#include <iostream>
#define INC(x) (++(x))
int main()
{
int a = 5;
INC(INC(a));
std::cout << a;
return 0;
}
while this code is perfectly legitimate 以及 well defined:
#include <iostream>
template<class T> T& INC(T& x) // sequence point after evaluation of the arguments
{ // 以及 before execution of the function body
return ++x;
}
int main()
{
int a = 5;
INC(INC(a));
std::cout << a;
return 0;
}
真的?
All this looks very much like a defect of the old C++ st以及ard.
Fortunately this has been addressed in newer C++ st以及ards (starting with C++ 2011) as there is no such concept as sequence point anymore. Instead there is a relation - something sequenced before something. And of course the natural guarantee that evaluation of the argument expressions of any operator is sequenced before evaluation of the result of the operator is there.
ISO/IEC 14882-2011 - 1.9 Program execution
Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread (1.10), which induces
a partial order among those evaluations. Given any two evaluations A
以及 B, if A is sequenced before B, then the execution of A shall
precede the execution of B. If A is not sequenced before B 以及 B is
not sequenced before A, then A 以及 B are unsequenced. [ Note: The
execution of unsequenced evaluations can overlap. — end note ]
Evaluations A 以及 B are indeterminately sequenced when either A is
sequenced before B or B is sequenced before A, but it is unspecified
which. [ Note: Indeterminately sequenced evaluations cannot overlap,
but either could be executed first. — end note ]
Every value computation 以及 side effect associated with a full-expression is sequenced before every value computation 以及 side
effect associated with the next full-expression to be evaluated.
Except where noted, evaluations of oper以及s of individual operators
以及 of subexpressions of individual expressions are unsequenced. [
Note: In an expression that is evaluated more than once during the
execution of a program, unsequenced 以及 indeterminately sequenced
evaluations of its subexpressions need not be performed consistently
in different evaluations. — end note ] The value computations of the
oper以及s of an operator are sequenced before the value computation of
the result of the operator. If a side effect on a scalar object is
unsequenced relative to either anotherside effect on the same scalar
object or a value computation using the value of the same scalar
object, the behavior is undefined.
[ Example:
void f(int, int);
void g(int i, int* v) {
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
f(i = -1, i = -1); // the behavior is undefined
}
— end example ]
(Also NOTE, How C++ 2003 prefix increment example i = ++i + 1;
, 改为“职位固定增减范例i = i++ + 1;
in this C++ 2011 quote.) iii