Pointer math

T

tfelb

Hi group!

I have here five different declarations but I have some problems to
understand this concept. I know there are more examples if
I would use parentheses but I think the following ones are common.

What I've learned..

int myArray[3] = { 0, 1, 2 };
int *ptr = myArray;

1. *ptr++
2. *++ptr
3. ++*ptr
4. ++*++ptr
5. ++*ptr++

1. printf("%d",*ptr++);

result: 0 first array element

because the postfix increment operator has a higher precedence
than the dereferencing operator. Associativity LEFT to RIGHT. That
would mean the same as *(ptr++) First
incrementing the pointer then the OLDER value will be fetched.

But why the older value? The incrementing process was the first
action?

2. printf("%d",*++ptr);

result: 1 second array element

The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT To LEFT.That
would mean *(++ptr) First incrementing the pointer then fetching
the value.

3. printf("%d",++*ptr)

result: 1 second array element

The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT to LEFT. The
would mean++(*ptr). First fetching the value THEN incrementing.

But why was the result 1 and not 0?

4. printf("%d",++*++ptr)

result: 2 the third array element

The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT to LEFT. That would mean first
incrementing the pointer THEN fetching the value THEN incrementing
that value?

5. printf("%d",++*ptr++);

result: 1 the second array element

The postfix operator has a higher precedence than the prefix and
dereferencing operator. That would mean
the same as ++*(ptr++). First the pointer will be increment then
fetching the value and then incrementing that
value. Here the Associativity is tricky.

I hope I'll get some clarity.

Thanks for any help!


Tom
 
M

maverik

Hi group!

I have here five different declarations but I have some problems to
understand this concept. I know there are more examples if
I would use parentheses but I think the following ones are common.

What I've learned..

int myArray[3] = { 0, 1, 2 };
int *ptr = myArray;

1. *ptr++
2. *++ptr
3. ++*ptr
4. ++*++ptr
5. ++*ptr++

1. printf("%d",*ptr++);

Ok, this is gets the value from ptr - it's 0, because after
int *ptr = myArray;

it points to the zeroth element. So printf prints 0, and the this
(local!) value is incremented.
2. printf("%d",*++ptr);

That's clear. ptr points to the zeroth element. After increment it
points to the first elemnt, so it's value is 1.
3. printf("%d",++*ptr)

That's easy too: ptr points to the zeroth element, you get it's value
and increments in, so you get 0 + 1 == 1.
4. printf("%d",++*++ptr)

After ++ptr ptr points to the first element of the array, you get its
value and increments it, so you get 2.
5. printf("%d",++*ptr++);

Here you get value of the ptr that points to the zeroth element,
increments it (prefix ++), the printf prints it (of course it's 1),
then the local value increments again (postfix ++).
 
T

tfelb

tfelb said:
Hi group!
I have here five different declarations but I have some problems to
understand this concept. I know there are more examples if
I would use parentheses but I think the following ones are common.
What I've learned..
int myArray[3] = { 0, 1, 2 };
int *ptr = myArray;
1. *ptr++
2. *++ptr
3. ++*ptr
4. ++*++ptr
5. ++*ptr++
1. printf("%d",*ptr++);
   result: 0 first array element
   because the postfix increment operator has a higher precedence
   than the dereferencing operator. Associativity LEFT to RIGHT. That
would mean the same as *(ptr++) First
   incrementing the pointer then the OLDER value will be fetched.
   But why the older value?

Because that's what (ptr++) means.
The incrementing process was the first
action?

That's not what it means.
You have two operations performed
to evaluate this expression: (*(ptr++)

1    an increment
2    a dereference

There is no sequential order
in which those operations are performed.

The significance of most object type expressions in a C program,
can be completely described in terms of their values
and side effects.

The side effects of both (p++) and (++p)
is that p is incremented.
The value of (++p), is the value after the increment;
the value of (p++), is the value before the increment,
but there is no order implied as to whether the increment
takes place first or whether the value is derived first.

Thank you!. I thought I could solve each expression with their
associativity and precedence?
 
B

Ben Bacarisse

Your question, I see, has been answered succinctly already but this
took too long to write not to post it!

What I've learned..

int myArray[3] = { 0, 1, 2 };
int *ptr = myArray;

1. *ptr++
2. *++ptr
3. ++*ptr
4. ++*++ptr
5. ++*ptr++

1. printf("%d",*ptr++);

result: 0 first array element

because the postfix increment operator has a higher precedence
than the dereferencing operator. Associativity LEFT to RIGHT.

Associativity is not involved. As you have said, postfix operators
like ++ have higher precedence than prefix ones like *. Associativity
only kicks in with operators with the same precedence. In fact, C
just defines expressions with a grammar so what are often called the
precedence and associativity of the operators are really just a
summary deduced from the grammar.
That
would mean the same as *(ptr++) First
incrementing the pointer then the OLDER value will be fetched.

But why the older value? The incrementing process was the first
action?

Because that is what ptr++ means. One gets added to ptr, maybe slowly
dragging it up to point to the next array element, but the value of
the expression ptr++ is always what it was before the change.

I say "slowly" because it helps to get rid of the idea that things
happen in sequence or even at some exact time. When evaluating ptr++,
ptr will eventually point to the next element, but you don't get to
know when this happens. C guarantees it will have happened by what is
called the next sequence point. In your case, this is just before the
printf function is called. An expression like ptr++ tells you two
things: that ptr will eventually point to the next element (by the
time next sequence point comes along) and that the value of the
expression will be what ptr had at the last sequence point (before any
change).

You will, sooner or later, have to lose the idea that a complex
expression can be broken down into an exact sequence of events. You
*can* break it down into what value the expression must yield, and
what (if any) changes to variables will have occurred by the next
sequence point. So, for example, printf("%d\n", *ptr++); fetches the
integer pointed at by ptr before this whole expression started
executing and it arranges that ptr will, by the time the function is
called, point to the next array element. The fact that ++ binds more
tightly than * does not mean that the effect of ++ must have happened
before the * is applied. It just means that the * us applied to the
value of ptr++ (which is its value at the last sequence point).
The ++ happens "eventually" (by the next sequence point).

Another way to look at it, is simply that ++x has the value x + 1 with
the side effect of incrementing x by the next sequence point. x++ has
the value x with the same side effect. All complex expressions can be
broken down into (a) the value they have and (b) the set of changes to
stored objects that have to happen by the next sequence point.

If all this seems too complex right now, just forget it for now and
re-read the first paragraph of my reply.
2. printf("%d",*++ptr);

result: 1 second array element

The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT To LEFT.That
would mean *(++ptr) First incrementing the pointer then fetching
the value.

The associativity tells you that *++ptr means (*(++ptr)). In this
case, there seems to a link between the brackets and the order things
happen, but that is what confused you above! The operator precedence
and associativity tell you what an un-braketed expression means, not
the order in which things happen. Of course, a compiler can "get it
right" by taking the simple option and following the order implied by
the brackets, but compilers can, and often do, choose to re-order
things if they think it will help.
3. printf("%d",++*ptr)

result: 1 second array element

The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT to LEFT. The
would mean++(*ptr). First fetching the value THEN incrementing.

But why was the result 1 and not 0?

Why not? *ptr refers to the first element of the array (it is what is
called an lvalue, which is why you can apply ++ to it). That element
contains 0 so ++ of it is 1. The ++ both changes the value and yields
this changed value.
4. printf("%d",++*++ptr)

result: 2 the third array element

No. The result is to print 2: the incremented value of the second
array element.
The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT to LEFT. That would mean first
incrementing the pointer THEN fetching the value THEN incrementing
that value?

Yes, the result must be as if we incremented ptr, followed that new
pointer value and incremented the variable we find there. The
compiler is permitted to first calculate ptr[1] + 1 (the final result)
and then arrange for the two increments to happen. It is likely that
the sequence is as you describe, but again (to labour the point) the
language only requires that the result be as if this sequence was
followed.
5. printf("%d",++*ptr++);

result: 1 the second array element

The postfix operator has a higher precedence than the prefix and
dereferencing operator. That would mean
the same as ++*(ptr++). First the pointer will be increment then
fetching the value and then incrementing that
value. Here the Associativity is tricky.

I hope I'll get some clarity.

I'll leave this one to you now! Apply the precedence and
associativity rules that you have already stated and you will get
there in the end. Break it down into the value it must have and,
separately, the changes that will be made by the time the printf call
happens.
 
A

Andrey Tarasevich

tfelb said:
int myArray[3] = { 0, 1, 2 };
int *ptr = myArray;

1. *ptr++
2. *++ptr
3. ++*ptr
4. ++*++ptr
5. ++*ptr++

1. printf("%d",*ptr++);

result: 0 first array element
Yes.

because the postfix increment operator has a higher precedence
than the dereferencing operator.
Yes.

Associativity LEFT to RIGHT.

Unary operators don't have any "associativity".
That
would mean the same as *(ptr++)
Yes.

First incrementing the pointer then the OLDER value will be fetched.

"First"? No. Neither operator precedence nor parentheses impose any
temporal ordering on the operator execution process. No one knows what
happens "first" or "next". The only thing that we know here is that
'ptr++' evaluates to the original value of 'ptr', and that by the next
sequence point the value of 'ptr' will get updated.
But why the older value?

Because that's what the specification of the postfix increment says.
Postfix increment always returns the original ("older") value of its
operand.
The incrementing process was the first action?

There's no such thing as "first action" here. Regardless of what happens
and when, postfix increment always returns the original value - that's
all you really need to know in this case.
2. printf("%d",*++ptr);

result: 1 second array element
Yes.

The dereferencing and the prefix operators have the same
precedence.

Er... No. There's no such thing as "precedence" for unary operators
specified on the same side of the operand. Both '*' and prefix '++' are
specified on the left. The one that "closer" to the operand ('ptr')
applies to the operand, the one that's further from the operand applies
to the result of the previous operator.
Associativity RIGHT To LEFT.

No. Again, there's no such thing as "associativity" with unary operators.
That would mean *(++ptr)

Yes. Again, simply because '++' is "closer" to 'ptr' than '*'.
First incrementing the pointer then fetching
the value.

Wrong again. It means that '*' is applied to the incremented value of
'ptr' (i.e. to 'ptr + 1'). When the value of 'ptr' will actually change
- no one knows. It will change by the next sequence point, that's for
sure. The compiler is free to evaluate it as follows

1. read *(ptr + 1)
2. change the value of 'ptr' to 'ptr + 1'

As you can see, the actuall incrementing of the pointer is the last (not
first) step in this case.
3. printf("%d",++*ptr)

result: 1 second array element

No. The result is the new value of the _first_ array element.
The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT to LEFT.

Both statement are wrong/irrelevant. See above.
The
would mean++(*ptr). First fetching the value THEN incrementing.

Same mistakes as before. What it really means is that '++' is applied to
the result of '*ptr'. Since this is the prefix '++', it is guaranteed
that it evaluates to the new value of the operand.
But why was the result 1 and not 0?

Because the new value is 1.
4. printf("%d",++*++ptr)

result: 2 the third array element

Again, no. The result is the new value of the _second_ array element.
The dereferencing and the prefix operators have the same
precedence. Associativity RIGHT to LEFT. That would mean first
incrementing the pointer THEN fetching the value THEN incrementing
that value?

Same mistakes. It means: calculate the new value of 'ptr', dereference
it, calculate the new value of that memory location; also update 'ptr'
and that memory location before the next sequence point.
5. printf("%d",++*ptr++);

result: 1 the second array element

No. The result is the new value of the _first_ array element.

All the same mistakes as above.
 
D

Doug Miller

Another way to look at it, is simply that ++x has the value x + 1 with
the side effect of incrementing x by the next sequence point. x++ has
the value x with the same side effect.

Very clearly and succinctly put. Thank you.
 
O

Old Wolf

Thank you!. I thought I could solve each expression with their
associativity and precedence?

Associativity and precedence are a part of parsing.
For example, they help you determine whether
the ++ refers to (*ptr) or just to ptr. They have
nothing to do with the definition of what the
++ operator actually _does_.
 
B

Barry Schwarz

tfelb said:
Hi group!
I have here five different declarations but I have some problems to
understand this concept. I know there are more examples if
I would use parentheses but I think the following ones are common.
What I've learned..
int myArray[3] = { 0, 1, 2 };
int *ptr = myArray;
1. *ptr++
2. *++ptr
3. ++*ptr
4. ++*++ptr
5. ++*ptr++
1. printf("%d",*ptr++);
   result: 0 first array element
   because the postfix increment operator has a higher precedence
   than the dereferencing operator. Associativity LEFT to RIGHT. That
would mean the same as *(ptr++) First
   incrementing the pointer then the OLDER value will be fetched.
   But why the older value?

Because that's what (ptr++) means.
The incrementing process was the first
action?

That's not what it means.
You have two operations performed
to evaluate this expression: (*(ptr++)

1    an increment
2    a dereference

There is no sequential order
in which those operations are performed.

The significance of most object type expressions in a C program,
can be completely described in terms of their values
and side effects.

The side effects of both (p++) and (++p)
is that p is incremented.
The value of (++p), is the value after the increment;
the value of (p++), is the value before the increment,
but there is no order implied as to whether the increment
takes place first or whether the value is derived first.

Thank you!. I thought I could solve each expression with their
associativity and precedence?

You can. You just have to realize that when an operator is applied to
its operand(s), an evaluation is performed. In the case of postfix
++, the evaluation always results in the non-incremented value.
Additionally, the incrementing that is performed is called a side
effect and the only thing you know about when it is performed is that
it will be completed before the next sequence point.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,965
Messages
2,570,148
Members
46,710
Latest member
FredricRen

Latest Threads

Top