Any good reason why assignment operator isn't a sequence point?

拟墨画扇 提交于 2019-11-27 14:20:58

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.

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!