cpointersprintfpost-incrementpre-increment

Why does a pointer to the first element of an array, after dereference, evaluates to the third element?


It dereferences to the third element when I'm trying to do it inside printf(), where I also try to dereference that pointer with post and pre-increment.

Here's my code:

#include <stdio.h>
int main()
{                                                                                                                
    int arr[4] = {1, 2, 3, 4};
    int *ptr = &arr[0];
    
    printf("ptr = %p, &arr[0] = %p\n", ptr, &arr); // same addrs
    printf("*ptr = %d\n", *ptr); // 1, as expected

    // PROBLEM IS HERE:
    printf("%d, %d, %d\n", *ptr, *ptr++, *++ptr); // 3, 2, 2  

    return 0;
}

I've tried funny things like adding elements to an array, changing order of expressions in printf to, but it gave me another confusing answer:

*++ptr, *ptr++, *ptr // 3, 1, 1

But, when I tried printfs in sequence, so to say:

printf("*ptr = %d\n", *ptr);
printf("*ptr++ = %d\n", *ptr++);
printf("*++ptr = %d\n", *++ptr);

Everything worked as expected.

What I expected from initial script in the last printf:

  1. *ptr = 1 Obviously, because it's the first element
  2. *ptr++ = 1 Firstly we dereference, and only then post-increment does its' thing
  3. *++ptr = 3 After previous incremention we are at the second element, now pre-increment goes first, hence we are at the addr of the third element, and, well, dereference gives us 3 - the value of the third element.

So then, why did I got 3, 2, 2?


Solution

  • Your program is invoking undefined behavior, which means anything can happen, so that you cannot rely on any particular behavior. What actually happens can depend on several things, such as which compiler you are using, which version of the compiler, what settings you are using for the compiler, etc.

    The reason why the behavior is undefined is because §6.5 ¶2 of the ISO C11 standard states the following:

    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.

    There is no sequence point between the evaluation of the individual arguments of a function call. This means that in your program, the arguments *ptr, *ptr++ and *++ptr of your printf function call are unsequenced with respect to each other. Two of those arguments (*ptr++ and *++ptr) change the value of ptr as a side-effect, and all three of those arguments use ptr in a value computation. Therefore, the behavior of your program is undefined according to the paragraph of the ISO C standard quoted above.

    See this related question for further information:

    Why are these constructs using pre and post-increment undefined behavior?