views:

292

answers:

5
+3  Q: 

Is this undefined?

Well, I'm not really in serious need of this answer, I am just inquisitive.

Expressions like *ptr++ = a are perfectly valid since we are operating on two objects ptr and *ptr but if i write *ptr++ = *ptr + a is it still valid ?

For example consider the following snippet:

int main(void){
   int a[] = {5,7,8,9,2};

   int* p =a;

   *p++ = 76; /*altering the first element */
   *p++ = *p + 32; /*altering the second element */    

   p = a;
   int i;
   for(i = 0;i<5; i++)
      printf("%d ",*p++);

   return 0;
}

I think that there is nothing to worry about with the expression *p++ = *p + 32; but I am unsure about the sequence points involved.

A: 

There are not the same if that what you're asking. It will compile though...

It is legal - if PTR points to an array, and not on the last cell of that array, so incrementing PTR will point to the next object in the array.

*ptr++ = *ptr + 2 is the same as *ptr = *ptr + 2, ptr++

Dani
I believe s/he's asking if it is defined in the C/C++ standard whether *ptr on the right of the = is the character pointed at before or after the ++.
ysth
What is the problem with `*ptr++ = a;` ? It is perfectly legal.
nthrgeek
@ysth: Well,I am he,)
nthrgeek
Sorry , the question was edited while answering.
Dani
A: 

I think it is undefined. However I'm not certain.

But, a bit of a broader stylistic point: if an expression makes you start to wonder if it's undefined, maybe this is a sign your code is not clear about its intentions and needs to be less ambiguous, and have fewer non-obvious dependencies on order of evaluation.

I used to think C was really cool because you could write a lot of very short statements that do lots of crazy stuff. I don't think that way anymore. :-)

asveikau
+11  A: 

The result of *ptr++ = *ptr + a is undefined. The equals sign is not a sequence point, so using the value of ptr again in that statement results in undefined behavior. Just consider the result if ptr is incremented before the RHS expression is evaluated, and compare it with the case where ptr is incremented after the RHS expression.

Note: this is not to say that the result of the expression will be from either of those two scenarios. Undefined is undefined.

Basically, you can only count on two things: that the post-increment is evaluated some time between the last sequence point and the next one, and that the expression ptr++ returns the value of ptr before it is incremented. So *ptr++ = a is fine because you can count on the result of ptr++.

int3
it is defined as add a to the *ptr, and have ptr point to the next element. the only question is if there is a next element.
Dani
No.. the idea is that you don't know what the value of `ptr` is, because you have incremented it somewhere else between the current pair of sequence points.
int3
`operator=` is a function call and a sequence point (as it `operator*` and `operator+`) But it's very likely `ptr` points to a built-in type, in which case the dereference, addition and assignment are not operators, not function calls and not sequence points.
MSalters
Actually.. I don't think that changes very much. The function call is a sequence point, but the order of evaluation of the arguments to the function is arbitrary. So his example would still be undefined. I actually think that overloaded operators would have exactly the same undefined behavior as built-in ones..
int3
Except with respect to the assignment side effect itself (which would happen inside the `operator=` and would thus be protected). But still, the increment of `ptr` on the left side and the access to `ptr` on the right side is undefined behavior (since the increment gets its value not from the access on the right side, but operates independently) even for a user defined operator function (as they are function arguments, like you say).
Johannes Schaub - litb
@litb: but all the side-effects would be happening inside one operator function or another.. what makes `operator=` special? If we make more than one assignment to the same variable between a pair of sequence points, we would still get undefined behavior.
int3
@int3 yes, but if no `operator=` is involved, you have *two* violations: 1) read `*ptr` while assigning to it, and 2) increment `ptr` while reading it. If `operator=` is involved, the first problem is gone (since assigning it is happens inside the operator function), but the second problem of course is not gone like you also say.
Johannes Schaub - litb
Ah wait, 1) is not a violation, because the modification to `*ptr` uses the value read by `*ptr` on the other side, the standard explicitly allows that (like in `a = a;`). So we are left with only 2), and a `operator=` won't change anything, so i agree with you. My argumentation is flawed anyway, since 1) would assume that in 2) the increment happens after reading `ptr` on the other side.
Johannes Schaub - litb
@Johannes Schaub - litb I know this is ancient but I'm not sure I follow. If we take the first clause of the undefined behavior definition this passes. *ptr is read once and written once, then ptr is modified. Nothing is modified more than once regardless of any sequence points in the expression so clause 1 passes. But clause 2 is not so clear to me: "Furthermore, the prior value shall be accessed only to determine the value to be stored." Is this what makes this expression undefined?
Dan Olson
For reference clause 1 as I refer to it above is: "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression." Note "modified", not "accessed".
Dan Olson
+5  A: 

First let us assume that 'p' is a pointer type.
Otherwise all the operation are just syntactic sugar for function calls.

Lets us break the statement down into parts.

int* p = a;

*p++ = *p + 32;

<< Sequence Point >>
// Part 1: p++
// Note the definition of post increment in the standard is (5.2.6)
// The result of the expression p++ is the value of 'p' while the value of the 
// objects represented by 'p' is incremented. This can be represented in pseudo code as:
(A) int*  p1 = p;
(B) p = p + 1;

// Part 2: *p (On the result of Part 1) (On *p++)
(C) int& p2 = *p1;  // Note the use of p1;

// Part 3: *p (On *p + 32)
// Note: There is no linkage between this use of 'p' and the 'p' in Part 1&2
(D) int& p3 = *p;

// Part 4: *p + 32;
(E) int p5 = p3 + 32; // Note the use of p3;

// Part 5: Assignment.
(F) p2 = p5;
<< Sequence Point >>

Ordering that must be preserved:
(A) Before (B)
(A) Before (C)
(D) Before (E)
(C) Before (F)
(E) Before (F)

Given the above constraints:
The compiler can re-order those instructions in several ways,
But the main point to note is that (B) can happen anywhere the only constraint on (B) is that it happen after (A) Thus the value of p3 as defined in (D) could be one of two different values depending on the exact position of (B).

As the value of p3 can not be defined here.
The resulting statement has undefined behavior.

Martin York
Excellent explanation.....
Richard Corden
+1  A: 

In terms of C, *ptr++ = *ptr + 32 would be undefined per 6.5.2: "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." You're both modifying and attempting to use the value of ptr in another computation without an intervening sequence point.

John Bode