Operator precedence in the given expressions
Asked Answered
R

6

12

Expression 1: *p++; where p is a pointer to integer.

p will be incremented first and then the value to which it is pointing to is taken due to associativity(right to left). Is it right?

Expression 2: a=*p++; where p is a pointer to integer.

Value of p is taken first and then assigned to a first then p is incremented due to post increment. Is it right?

Ruder answered 25/5, 2017 at 9:26 Comment(3)
Very good reference link : denniskubes.com/2012/08/14/do-you-know-what-p-does-in-cSamiel
Possible duplicate of Post-increment on a dereferenced pointer?Narvaez
Also Pointer expressions: *ptr++, *++ptr and ++*ptr. Or maybe What happens when you dereference a postincrement C (for the second expression).Narvaez
F
18

First of all, let me tell you that, neither associativity nor order of evaluation is actually relevant here. It is all about the operator precedence. Let's see the definitions first. (emphasis mine)

  • Precedence : In mathematics and computer programming, the order of operations (or operator precedence) is a collection of rules that reflect conventions about which procedures to perform first in order to evaluate a given mathematical expression.

  • Associativity: In programming languages, the associativity (or fixity) of an operator is a property that determines how operators of the same precedence are grouped in the absence of parentheses.

  • Order of evaluation : Order of evaluation of the operands of any C operator, including the order of evaluation of function arguments in a function-call expression, and the order of evaluation of the subexpressions within any expression is unspecified, except a few cases. There's mainly two types of evaluation: a) value computation b) side effect.


Post-increment has higher precedence, so it will be evaluated first.

Now, it so happens that the value increment is a side effect of the operation which is sequenced after the " value computation". So, the value computation result, will be the unchanged value of the operand p (which again, here, gets dereferenced due to use of * operator) and then, the increment takes place.

Quoting C11, chapter §6.5.2.4,

The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. The value computation of the result is sequenced before the side effect of updating the stored value of the operand. [.....]

The order of evaluation in both the cases are same, the only difference is, in the first case, the final value is discarded.

If you use the first expression "as-is", your compiler should produce a warning about unused value.

Forgat answered 25/5, 2017 at 9:27 Comment(0)
K
9

Postfix operators have higher priorities than unary operators.

Thus this expression

*p++

is equivalent to the expression

*( p++ )

According to the C Standard (6.5.2.4 Postfix increment and decrement operators)

2 The result of the postfix ++ operator is the value of the operand. As a side effect, the value of the operand object is incremented (that is, the value 1 of the appropriate type is added to it). See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. The value computation of the result is sequenced before the side effect of updating the stored value of the operand.

So p++ yields the original value of the pointer p as the result of the operation and has also a side effect of incrementing the operand itself.

As for the unary operator then (6.5.3.2 Address and indirection operators)

4 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined

So the final result of the expression

*( p++ )

is the value of the object pointed to by the pointer p that also is incremented due to the side effect. This value is assigned to the variable a in the statement

a=*p++;

For example if there are the following declarations

char s[] = "Hello";
char *p = s;
char a;

then after this statement

a = *p++;

the object a will have the character 'H' and the pointer p will point to the second character of the array s that is to the character 'e'.

Katabolism answered 25/5, 2017 at 9:41 Comment(0)
T
5

Associativity is not relevant here. Associativity only matters when you have adjacent operators with the same precedence. But in this case, ++ has higher precedence than *, so only precedence matters. Because of precedence, the expression is equivalent to:

*(p++)

Since it uses post-increment, p++ increments the pointer, but the expression returns the value of the pointer before it was incremented. The indirection then uses that original pointer to fetch the value. It's effectively equivalent to:

int *temp = p;
p = p + 1;
*temp;

The second expression is the same, except it assigns the value to another variable, so that last statement becomes:

a = *temp;
Thunderhead answered 25/5, 2017 at 9:34 Comment(2)
Not equivalent. The original has no sequence points, your rewrite does.Cai
Since it's not part of any larger expression, what difference does that make? I'm just decribing the observable results.Thunderhead
T
3

The expression

*p++

is equivalent to

*(p++)

This is due to precedende (i.e.: the postfix increment operator has higher precedence than the indirection operator)

and the expression

a=*p++

is for the same reason equivalent to

a=*(p++)

In both cases, the expression p++ is evaluated to p.

Tantra answered 25/5, 2017 at 9:34 Comment(1)
You didn't answer the question about what order things happen.Cai
P
1
  • v = i++;: i is returned to the equality operation and then assigned to v. Subsequently, i is incremented (EDIT: technically it's not necessarily executed in this order). Thus v has the old value of i. I remember it like this: ++ is written last and therefore happens last.
  • v = ++i;: i is incremented, and then returned to be assigned to v. v and i has the same value.
  • When you don't use the returned value, they do the same (although different implementations may yield different performance in some cases). E.g. in for loops, for(int i=0; i<n; i++) is the same as for(int i=0; i<n; ++i). The latter is sometimes automatically preferred because it tends to be faster for some objects.
  • * has lower precedence than ++ so *p++ is the same as *(p++). Thus in this case p is returned to * which dereferences it. Then the address in p is incremented by one element. *++p increments the adress of p first, then dereferences it.
  • v = (*p)++; sets v equal to the old value pointed to by p and then increments it, while v = ++(*p); increments the value pointed to by p and then sets v equal to it. The address in p is unchanged.

Example: If,

int a[] = {1,2};

then

int v = *a++;

and

int v = *++a;

will both leave a incremented, but in the first case v will be 1 and in the latter it'll be 2.

Pinky answered 25/5, 2017 at 9:37 Comment(2)
Wrong. "Then" and "Subsequently" promise a particular order of execution, but this code doesn't have sequence points, no order is guaranteed.Cai
Well, often implementations of the post-increment operator first increments and then returns a copy of the old value. For PODs I suppose it's up to the compiler (which might optimize anyway). So you're right, of course. However, abstractions are there so you can ignore the exact details of how something work and to think of it in simpler ways. I thougt of "then" and "subsequently" not necessarily in terms of order of execution but in more abstract terms. But perhaps if I were a native speaker I would not do that, so feel free to point that out.Pinky
C
0

*p++; where p is a pointer to integer.

p will be incremented first and then the value to which it is pointing to is taken due to associativity (right to left). Is it right?

No. In a post-increment, the value is copied to a temporary (an rvalue), then the lvalue is incremented as a side effect.

a=*p++; where p is a pointer to integer.

Value of p is taken first and then assigned to a first then p is incremented due to post increment. Is it right?

No, that's not correct either. The increment of p might happen before the write to a. What's important is that the value being stored in a was loaded using the temporary copy of the prior value of p.

Whether that memory fetch occurs before the memory write with the new value of p isn't specified, and any code that relies on the order is undefined behavior.

Any of these sequences are allowed:

  • Copy p into temporary THEN increment p, THEN load value at address indicated in temporary THEN store loaded value to a
  • Copy p into temporary THEN load value at address indicated in temporary (this value itself is placed in a temporary) THEN increment p THEN store loaded value to a
  • Copy p into temporary THEN load value at address indicated in temporary THEN store loaded value to a THEN increment p

Here are two code examples that are undefined behavior because they rely on the order of side effects:

int a = 7;
int *p = &a;
a = (*p)++;  // undefined behavior, do not do this!!

void *pv;
pv = &pv;
void *pv2;
pv2 = *(pv++);  // undefined behavior, do not do this!!!

The parentheses do not create a sequence point (or sequenced before relationship, in the new wording). The version of the code with parentheses is just as undefined as the version without.

Cai answered 25/5, 2017 at 17:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.