Is there any good reason for operator =
not being a sequence point? Both in C and C++.
I have trouble thinking about an counter-example.
Is there any good reason for operator =
not being a sequence point? Both in C and C++.
I have trouble thinking about an counter-example.
By request:
In general, things need a reason to be a sequence point. They don't need a reason not to be a sequence point; that's the default.
For example, &&
must be a sequence point because of short-circuiting behaviour: if the left-hand side is false, the right-hand side must not be evaluated. (This is not just about optimization; the right-hand side could have side effects, and/or depend on the left-hand side being true, as in ptr && ptr->data
.) Therefore the left-hand side must be evaluated first, before the right-hand side, in order to see if the right-hand side should be evaluated at all.
This reason does not exist for =
because, although there is "evaluation" to do for both sides (although there are different restrictions on what can appear on both sides: the left-hand side must be an lvalue - the l
doesn't stand for "left", btw; it stands for "location", as in location in memory - we can't assign to a temporary or a literal), it doesn't matter which side is evaluated first - as long as both sides are evaluated before the actual assignment.
=
not being a sequence point did bite me once: I had myArray[i++] = <expression containing i>;
, and one of two compilers calculated the RHS "after" the LHS, producing the wrong answer. My fault in the end, but tricky nonetheless. –
Aerobiology (((ch = *x++) != 0) && (*y++ = ch))
could, but for the explicit requirement that && imposes a sequence point, delay the increment of x until after the execution of the second part of the expression, since the compiler could determine before incrementing x whether or not the left-hand operand of &&
would evaluate to zero. In practice, it's unlikely that any of the code rearrangements prohibited by the sequence point would otherwise be useful, but in some theoretical cases they could be. –
Figueroa x = y[x] = 0
? Is it guaranteed that y[x]
will evaluate (using the old value of x) before x
gets set to 0? –
Professorate y[x] = 0
in advance of determining the address to write to, as that value is simply the right hand side (or its implicit conversion to the of type of y[x]
). Provided that the compiler allows itself to determine that type without first resolving the actual address to write to, there's nothing holding back the compiler from performing that optimization and creating undefined behavior. –
Pitzer It is (sort of). The operator= (that can be defined by the engineer (aka the user defined operator= for class types)) is just syntactic sugar for a function call. As a result it has the same "sequence point" semantics as a function call.
If we are taking about built in types then I think it is a good thing.
You don't want to introduce too many sequence points as this hinders optimizations.
operator=
will be the LHS and RHS of the =
sign, and function calls don't impose order on the evaluation of their arguments - just the evaluation of the function itself is a sequence point. –
Walloping There are many reasons not to require either side to be evaluated before the other. A more interesting question is whether the evaluation of both sides, complete with side-effects, should be required before the assignment operator itself does anything. I would suggest that such a requirement would ease some aliasing restrictions but in some cases require more work for a compiler. For example, suppose "foo" and "bar" are pointers to large structures whose addresses would overlap. The statement "*foo = *bar;" would represent undefined behavior under the current standard. If there were a sequence point between the evaluation of the operands and the assignment, such a statement would be guaranteed to "work". Such a guarantee would require more complicated for the assignment operator, requiring larger and slower code even if in practice the pointers will never overlap.
Example:
unsigned char foo[100]; typedef struct {int x, int y;} POINT; POINT *p1 = (POINT*)foo; POINT *p2 = (POINT*)(&(p1->y));
Given the above declarations, I think the following statements have the strictly-defined behaviors indicated and do not involve any Undefined Behavior.
p1->y = somevalue; // Sets p2->x to somevalue p2->x = somevalue; // Sets p1->y to somevalue *p1 = mystruct; // Sets p2->x to mystruct.y *p2 = mystruct; // Sets p1->x to mystruct.x
The following two statements, however, would involve Undefined Behavior:
*p1 = *p2; *p2 = *p1;
If there were a sequence point at the equals sign, a compiler would have to either compare p1 and p2, or else copy the source operand to a temp location and then copy it to the destination. The standard, however, makes clear that the above two statements are both considered to be Undefined Behavior. The standard requires compilers to generate code which works correctly when copying a structure to a non-overlapping structure, but places no restriction on what compilers may do if the structures overlap. A compiler which would the processor into a loop sending "Frink Rules!" out every open TCP socket would not violate the standard by so doing.
unsigned char [100]
is correctly aligned for ... anything that requires alignment? "Are you suggesting that a structure would require alignment larger than any element therein?" Maybe. –
Stencil It is since c++17. See this for details.
© 2022 - 2024 — McMap. All rights reserved.
ptr && ptr->data
to work. Rather, because&&
is required by the Standard to have short-circuit behaviour: the Standard says that if the left-hand side evaluates to false, the right-hand side must not be evaluated at all. Therefore, it is not allowed to evaluate the right-hand side first, in case the left-hand side is false. :) – Walloping=
as well, just in the opposite direction, you need to evaluate the right side to be able to assign it to the right side. But because=
isn't a sequence pointi++ = i++
isn't defined unlikei++ && i++
which is. – Dwan=
, you must evaluate both the left and the right side, and then do the assignment (Just that there are somewhat different rules for "evaluating" each side - lvalues vs. rvalues, etc.). But there is no reason you have to evaluate the left side before the right side, or vice versa - as long as you do both before the actual assignment. With&&
, you must evaluate the left side before the right side, because it's possible that the right side must not be evaluated at all. – Walloping