Pointer arithmetic; Must do it twice initally

129 views Asked by At

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.

5

There are 5 answers

5
John M On BEST ANSWER

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

    //..............................
}
0
Eugene Sh. On

You are encountering an undefined behaviour here. By taking the address of j into p you are getting the address of j only, and by no means of the beginning of a. The line int j = a[0]; is assigning the value of the first as element to the j, but not its address. And by incrementing p you are going out of bounds of the defined memory space and thus invoking UB.

0
Andrew McGuinness On
 int j = a[0]

does not make j an alias for the first element of the array a, it defines a new int variable j, which is initialised to a copy of the first element of a. Therefore

 p = &j;

p is not pointing at any element of a, but at the separate variable j.

1
Xofo On

You must remember how to make pointers point to a part of an array. What you are doing is assigning the value to the address of an integer (which points to element 0 of said array ... trouble).

Here is an example:

#include <stdio.h>

int main(void) 
{

  int a[4] = {5,2,6,4};

  /* Always initialize your variables */
  int *p = NULL;
  int i = 0;

  p = a;

      for (i=0; i < 4; i++)
      {
        printf("Number is = %d,\n", p[i]); // or p + i * sizeof(int)

      }

  return 0;
  }




---Results are----

./a.out 
Number is = 5,
Number is = 2,
Number is = 6,
Number is = 4,
0
Tlacenka On

Since I cannot add comments, this is the only way I can react to previous answers, which I myself influenced.

Xofo: Actually, I am sorry, I confused you as much as I confused myself. It of course could be p[i] like I pointed out, but the second edit (in the commentary) is wrong. p[i] = *(p + i), therefore your version was right as well. If it was to contain sizeof(int), it would have to be something like *(p + [sizeof(int) * i]), which I am not sure if that is even possible in C.