2

I have a question about array of pointers in C: assume we have the following code

int arr[] = {1,2,3};
int *parr[3];

for(int i = 0; i<3; i++)
    parr[i] = &arr[i];

Why are *(*(parr + 2)) and *(*parr +2) equal and both pointing to the 3rd element in arr? I understand the first one but not the second expression.

1
  • Because the block of RAM is contiguous and the + operator uses sizeof(int *) on the left side of this: &parr[0] + 2 == &parr[0 + 2] Commented Aug 23, 2019 at 21:00

3 Answers 3

2

Here is your arr in memory:

  Content               Address
+---------------------+
| arr[0] = 0x00000001 | 0xaaaa00000000 = arr
| arr[1] = 0x00000002 | 0xaaaa00000004
| arr[2] = 0x00000003 | 0xaaaa00000008
| ...                 | 0xaaaa00000010

Here is yout parr in memory:

  Content                    Address
+--------------------------+
| parr[0] = 0xaaaa00000000 | 0xbbbb00000000 = parr
| parr[1] = 0xaaaa00000004 | 0xbbbb00000008
| parr[2] = 0xaaaa00000008 | 0xbbbb00000010
| ...                      | 0xbbbb00000018

When you do *(*(parr + 2), you are getting:

*(*(parr + 2))   *(*(0xbbbb00000000 + 2*sizeof(int*)))
== *(parr[2])    *(*(0xbbbb00000010))
== *(&arr[2])    *(0xaaaa00000008)
== arr[2]        0x00000003

When you do *(*parr + 2), you are getting:

*(*parr + 2)        *(*(0xbbbb00000000) + 2*sizeof(int))
== *((&arr[0])+2)   *(0xaaaa00000000 + 2*sizeof(int))
== *(arr+2)         *(0xaaaa00000008)
== arr[2]           0x00000003

You may be wondering why arr + 2 is not equal to 0xaaaa00000000 + 2 == 0xaaaa00000002, that's because pointer arithmetic is different than normal integer arithmetic. When you do this:

int arr[] = {1, 2, 3}

That's an array of 3 integers, each sizeof(int) == 4 bytes long, so 12 bytes in total. You can see this clearly from my drawing above.

When you do:

arr + 1

That's skipping sizeof(int) == 4 bytes, thus skipping the first integer to get to the second.

The compiler does all of this for you transparently,so you don't really notice it. Each time you do something like arr[i], it really is *(<address_of_arr> + i * sizeof(<type>)).

Sign up to request clarification or add additional context in comments.

2 Comments

Perfect, Thanks a lot!
You're welcome. Added a bit of explanation on pointer arithmetic.
1

I don't have reputation to comment :)

I have a little note.

when the C compiler encounter an expression like a[i] it's transform it to an expression like *(a+i).

1 Comment

Now that is a good little note, which nowhere in my overly long answer did I manage to mention!
1

You've got a lot of pointers here. Take a deep breath.

*(*(parr + 2)) says, "take the second element of parr, and dereference it". This is equivalent to *parr[2].

*(*parr + 2) says, "take the first pointer pointed to by parr, add 2 to it, and dereference". It is equivalent to *(parr[0] + 2).

This all works because parr isn't a simple pointer to int (which is the type of pointer we usually use to access arrays of int), but rather, an array of three pointers to int. The situation looks like this:

      +-----+-----+-----+
 arr: |  1  |  2  |  3  |
      +-----+-----+-----+
         ^     ^     ^
         |     |     |
      +--|--+--|--+--|--+
parr: |  *  |  *  |  *  |
      +-----+-----+-----+

So it's easy to see how *parr[2] works; it just chases the third pointer in parr.

To see how the other expression works, let's first throw an ordinary pointer-to-int into the mix. Let's write

int *ip = arr;

(This is, as I said earlier, "the type of pointer we usually use to access arrays of int".) This gives us

      +-----+-----+-----+
 arr: |  1  |  2  |  3  |
      +-----+-----+-----+
         ^
         |
      +--|--+
  ip: |  *  |
      +-----+

Now, it's a hallmark of C that a pointer-to-int accesses an array-of-int perfectly, just as well as the original array did. (Actually this is true for any pointer-to-T and array-of-T.) p points to a[0], but by pointer arithmetic, p+1 points to a[1], and p+2 points to a[2].

In fact, when we say *p, that's actually p[0], which is actually a[0]. When we say *(p+1), that's actually p[1], which is actually a[1]. When we say *(p+2), that's actually p[2], which is actually a[2]. Etc.

But the ip I defined is the same pointer as your parr[0]. So *(*parr + 2) is *(ip + 2) which is a[2].

Now, I blew past something which it sounds like you haven't really learned yet. I said,

It's a hallmark of C that a pointer-to-int accesses an array-of-int perfectly, just as well as the original array did.

By definition, when I have a pointer-to-type-T, and I add 1 to that pointer, the compiler makes it point to the next element in a conceptual array of T. So under the hood, the compiler doesn't add 1 to the address, it actually adds sizeof(T) to the address. For a pointer to int, on a 32-bit machine, that means adding 4, as you saw.

How does the compiler know how to do this? Well, this is the reason that pointers have types, the reason that they know what type they point to. When we declare

int *ip;

we are not just declaring ip to be a pointer; we are very specifically declaring it to be a pointer to int. So the compiler always knows it points to ints, so it always knows to multiply by 4 when doing pointer addition (and to divide by 4 when doing pointer subtraction, but that's another story).

And if this was your question, you need to skip to the chapter in your C book that talks about pointer arithmetic in C, and the special relationship between pointers and arrays. There are zillions of questions and answers here on Stack Overflow discussing that relationship, but I'm not sure which is the canonical one, so just now I can't point you at one.

5 Comments

what confuses me really is, in the 2nd expression *(*ptr + i), *ptr is basically an address VALUE, say 0xFFFF, when incrementing it, why does it increase by 4 as an integer pointer, I know its an address of an integer, but HOW does it know?
Thats perfect, thanks! so basically if it was an array of char pointers, char * carr[3], and i pointed to the int array, *(*carr +1) would point to the 2nd byte of the first element? Am I right?
Um, right. Or if you declared a simple char *cp and pointed it to the array. (Whether it's a good idea to be pointing at the individual bytes in an int is another question. You can and sometimes need to, but then you get to learn about endianness...)
However if i use char * cp and pointed in the array, accessing the 2nd byte would be *(cp + i) I guess, since really cp = *carr = carr[0] I would say.
Just so. (Both your parr and your carr are foreign to me because they contain unnecessary, redundant information. Basically parr[1] precomputes -- or caches the result of -- parr[0] + 1.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.