Reputation: 1175
Consider this example:
T *fun(T *x) {
// do something with contents of (*x), do not change x itself
return x;
}
T var = ...;
var = *fun(&var); // <- is this valid?
The highlighted line ultimately takes a pointer to a variable, dereferences it, and copies the contents of the memory pointed to by the pointer into the same location
Is this allowed? If this trips UB, why? Can this be considered an example of aliasing?
Upvotes: 1
Views: 70
Reputation: 81257
C compilers are generally required to allow for the possibility that an lvalue on the right side of an assignment operator might identify the same object an lvalue on the left which has the same type and is accessed in similar fashion. They are not generally required to allow for the possibility that an lvalue used on the right side of an expression might interact with one on the left side via other means. Among other things, given something like:
void doSomething(long *p, long *q, long *r)
{
*r = *p + *q;
}
a compiler could load the bottom half of *p
, add the bottom half of *q
, store the result to the bottom half of *r
, and then load the top half of *p
, add the top half *q
along with any carry, and store the result to the top half of *r
. This could malfunction if e.g. the bottom word of *r
occupied the same storage as the top word of *q
, even if code would never try to read *q
after the assignment was processed.
If a compiler has no reason to think that an assignment will modify any of the lvalues used in the source, it may perform additional optimizations. For example, if a compiler where _Bool
is one byte is given something like:
int x;
void handleAssign(char *p)
{
x += p[0]*p[1] + p[2]*p[3];
}
it would be entitled to process it as though it were written:
int temp;
void handleAssign(char *p)
{
x += p[0]*p[1]; // Using wrapping two's-complement math
x += p[2]*p[3]; // Using wrapping two's-complement math
}
which might improve efficiency on platforms that could directly add the result of the multiply to a memory operand but would otherwise have to transfer it to another register, add the result of the second multiply to that register, and then add that result to x. Such processing is separate from type-based aliasing.
Upvotes: 0
Reputation: 127
Yes it's valid the new value of var is only assigned after the right side of the expression is completely eveluated.
Upvotes: 0
Reputation: 224917
This is fine. The expression has a side effect of updating var
, but this happens after evaluating the value of var
on the right side since the right side has to be evaluated before var
can be updated.
What you're doing is effectively the same as:
var = var;
It would also be fine if func
modified *x
because a function call introduces a sequence point.
Expressions such as this are mentioned in section 6.5p2 of the C standard:
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.84)
84)) This paragraph renders undefined statement expressions such as
i = ++i + 1; a[i++] = i;
while allowing
i=i+1; a[i] = i
Upvotes: 1