Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using pointed to content in assignment of a pointer

It has always been my understanding that the lack of a sequence point after the reading of the right expression in an assignment makes an example like the following produce undefined behavior:

void f(void)
{
   int *p;
   /*...*/
   p = (int [2]){*p};
   /*...*/
}
// p is assigned the address of the first element of an array of two ints, the
// first having the value previously pointed to by p and the second, zero. The
// expressions in this compound literal need not be constant. The unnamed object
// has automatic storage duration.

However, this is EXAMPLE 2 under "6.5.2.5 Compound literals" in the committee draft for the C11 standard, the version identified as n1570, which I understand to be the final draft (I don't have access to the final version).

So, my question: Is there something in the standard that gives this defined and specified behavior?

EDIT

I would like to expound on exactly what I see as the problem, in response to some of the discussion that has come up.

We have two conditions under which an assignment is explicitly stated to have undefined behavior, as per 6.5p2 of the standard quoted in the answer given by dbush:

1) A side effect on a scalar object is unsequenced relative to a different side effect on the same scalar object.

2) A side effect on a scalar object is unsequenced relative to a value computation using the value of the same scalar object.

An example of item 1 is "i = ++i + 1". In this case the side effect of writing the value i+1 into i due to ++i is unsequenced relative to the side effect of assigning the RHS to the LHS. There is a sequence point between the value calculations of each side and the assignment of RHS to LHS, as described in 6.5.16.1 given in the answer by Jens Gustedt below. However, the modification of i due to ++i is not subject to that sequence point, otherwise the behavior would be defined.

In the example I give above, we have a similar situation. There is a value computation, which involves the creation of an array and the conversion of that array to a pointer to its first element. There is also a side effect of writing a value to part of that array, *p to the first element.

So, I don't see what gaurantees we have in the standard that the modification of the otherwise uninitialized first element of the array will be sequenced before the writing of the array address to p. What about this modification (writing *p to the first element) is different from the modification of writing i+1 to i?

To put it another way, suppose an implementation looked at the statement of interest in the example as three tasks: 1st, allocate space for the compound literal object; 2nd: assign a pointer to said space to p; 3rd: write *p to the first element in the newly allocated space. The value computation for both RHS and LHS would be sequenced before the assignment, as computing the value of the RHS only requires the address. In what way is this hypothetical implementation not standard compliant?

like image 462
Kyle Avatar asked Feb 12 '18 20:02

Kyle


1 Answers

You need to look at the definition of the assignment operator in 6.5.16.1

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.

So here you clearly see that first it evaluates the expressions on both sides in any order or even concurrently, and then stores the value of the right into the object designated by the left.

Additionally, you should know that LHS and RHS of an assignment are evaluated differently. Citations are a bit too long, so here is a summary

  • For the LHS the evaluation leaves "lvalues", that is objects such as p, untouched. In particular it doesn't look at the contents of the object.

  • For the RHS there is "lvalue conversion", that is for any object that is found there (e.g *p) the contents of that object is loaded.

  • If the RHS contains an lvalue of array type, this array is converted to a pointer to its first element. This is what is happening to your compound literal.

Edit: You added another question

What about this modification (writing *p to the first element) is different from the modification of writing i+1 to i?

The difference is simply that i in the LHS of the assignment and thus has to be updated. The array from the compound literal is not in the LHS and thus is of no concern for the update.

like image 172
Jens Gustedt Avatar answered Sep 30 '22 23:09

Jens Gustedt