Search code examples
cpointerspointer-arithmetic

Pointer arithmetic; Must do it twice initally


I have a piece of code here that I'm a bit confused about. It seems that if I use: p = &j, it takes two increments just to begin traversing a[]:

#include <stdio.h>

int main(void) {
  int a[4] = {5,2,6,4};
  int j = a[0];
  int *p;

  p = &j;

  printf("*p = %d\n", *p);
  printf("a[0] = %d\n", a[0]);
  printf("address of a[0] = %p\n", &a[0]);

  printf("\np++\n\n");
  p++;

  printf("*p = %d\n", *p);
  printf("a[1] = %d\n", a[1]);
  printf("address of a[1] = %p\n", &a[1]);

  printf("\np++\n\n");
  p++;

  printf("*p = %d\n", *p);
  printf("a[2] = %d\n", a[2]);
  printf("address of a[2] = %p\n", &a[2]);

  return 0;
}

If I use p = &a[0], then it works fine. Why is it when I use &j, it takes two p++'s to start. You can see *p = 5 twice before it starts incrementing through the array. Why is this? Any help would be appreciated.


Solution

  • This is a really cool bug to run into, especially for someone who is just beginning to learn C. Understanding this seemingly strange behaviour will improve your understanding of how your programs work quite a bit.

    Your code does not work properly because int j and a[0] do not have the same address, that is to say, &j != &a[0].

    Let me use a graphic to explain why.

    All of your local variables a[], j, p, etc... are allocated on your program's call stack.

    So, in your example, the stack may hypothetically look like this:

    enter image description here

    You can see from this wonderfully artistic diagram that the address of a is 0x1004 whereas address of j evaluates to 0x1000.

    Just because 2 variables have the same value does not imply that they will have the same memory address. That is what's happening here. You give j the value of a[0], but they are still 2 seperate, unrelated pieces of memory.

    The mysterious behaviour of why incrementing the pointer puts you at the start of a[] can be explained by the fact that both j and a[] are allocated on the stack. j's address will probably be right below a[]'s address. Indeed, as you can see in my magnum opus of art, if we were to move upwards 4 bytes from &j, we would arrive at &a[].


    Edit 1 as per Eugene Sh.:

    PLEASE NOTE: incrementing a pointer like this is undefined behaviour in a C program. The ordering of variables on the stack is undefined and depends on the compiler, you may get a different result every time your code recompiles. Please please never never ever ever do this in a real program. Doing p++ will result in undefined behaviour.

    Edit 2:

    You could carry out a cool experiment by printing out the addresses of all your variables.

    int main()
    {
        int a[4] = {5, 2, 6, 4};
        int j = a[0];
        int* p;
    
        //in printf, %p stands for "print pointer"
        printf("&a[0] = %p\n", &a[0]);
        printf("&j    = %p\n", &j);
        printf("&p    = %p\n", &p);     //the address of a pointer.... craaazy
        printf("p     = %p\n", p);      //should print same address as &j
    
        //..............................
    }