0

I was comparing various printf to better understand the difference between int * and int (*)[] and how i can visualize various addresses and values.

In the following program i wrote one thing bothers me:

#include <stdio.h>
    
int main() {
    int a[3] = {1,2,3};
    int *p = a;                             
    
    printf("address of a:  %p\n", &a);
    printf("\naddress of a[0]:  %p\n", &a[0]);
    printf("\nvalue of p:  %p\n", p);
    printf("\nvalue of *p:  %d\n", *p);
    printf("\nvalue of a[1]:  %d\n", *(p + 1));
    
    puts("\n\n-------------------\n\n");
    
    int b[3] = {1,2,3};
    int (*q)[3] = &b;
    
    printf("address of b:  %p\n", &b);
    printf("\naddress of b[0]:  %p\n", &b[0]);
    printf("\nvalue of q:  %p\n", q);
    printf("\nvalue of *q:  %p\n", *q);
}

In the first part p, as a pointer, holds as value the address of a[0], so *p holds as value 1 ( printed with %d).

In the second part, though, q seems to hold the same value of *q (in this case i use %p), therefore i need to use **q to print b[0].

How come?

5
  • An array and its first element have the same address, but the type of corresponding pointers is different. Commented Aug 17, 2022 at 8:57
  • "so *p holds as value 1"... No, *p POINTS at the value (that happens to be 1)... p is a variable whose datatype is pointer to int meaning p can hold a memory address (point to), in this case, an int... I hope this helps... Commented Aug 17, 2022 at 9:10
  • As a bonus, you could also observe the value of q + 1 (not *(q + 1), beware), compared to p + 1. Please note that you should cast to void *, when using %p. Commented Aug 17, 2022 at 9:11
  • int *p = a; is exactly the same as (shorthand for) int *p = &a[ 0 ]; Perhaps seeing this will make it more obvious that p is assigned the address of the first element of a[ ]... Commented Aug 17, 2022 at 9:13
  • @Lundin yes, you are right. Thank you for the correction Commented Aug 17, 2022 at 9:33

2 Answers 2

2

The pointer q points to an array of 3 integers. You can visualise it like this:

                 
  q -----> |b = {1,2,3}|
                
 
// q points to the whole array.
// note how it doesn't point to a specific element.

Your print statements broken down:

  • &b - this is the base address of b.
|b = {1,2,3}| // address of whole array
  • &b[0] - this is the address of the 0th element of b.
   b = {1,2,3}
        ^
// address of b[0]
  • q - this points to the base address of b and holds the same value as &b.
q -----> |b = {1,2,3}| // address of whole array
  • *q - this will yield the address of the first element of b, in your case this is the address of b[0].
 b = {1,2,3}
*q ---^

In regards to your question:

With q you must dereference twice (**q) because:

  • q points to the base address of b
  • if we dereference once (*q) it will yield the address of b[0].
  • if we dereference once more (**q) we will get the value in b[0].

Here is a visualization that may help you understand how it works.

- q -----> |b = {1,2,3}|  // points to base address of b
- *q ------------^ // points to address of b[0]
- **q -----------^ // value of b[0]
Sign up to request clarification or add additional context in comments.

2 Comments

"*q - this will yield the address of the first element of b" --> *q is the array. Converting to the address of the first element of b depends on the surrounding code, like another * as in **q.
OP has printf("\nvalue of *q: %p\n", *q); in the code so in this case it will yield a implicitly decayed pointer to the first element of b which is b[0].
1
  • When you dereference a plain pointer like int* you get an item of type int.
  • When you dereference an array pointer like int (*q)[3] you get an array of type int [3].

Now, whenever you use an array in an expression (in most cases) it decays into a pointer to its first element. So *q gives you an array int [3] which then immediately decays into a pointer to the first element, type int*. And that's the pointer you'll be printing. We can prove that this is the case by executing this snippet:

_Generic(*q, 
         int*:      puts("we ended up with an int*"), 
         int(*)[3]: puts("this wont get printed") );

In order to print the value pointed at by that pointer, you therefore need another level of dereferencing: either (*q)[0] or **q.

7 Comments

I see, and that's also the same reason i need to write *(*q + 1) for b[1] instead of *(q + 1), is that correct?
@Talete Kind of, yes. In case of *q + 1 pointer arithmetic is carried out on type int* but in case of q + 1 it is carried out on type int (*)[3] And in the latter case + 1 means + the size of a whole int [3] array.
"*q gives you an array int [3] which then immediately decays into a pointer to the first element," is a bit off on the immediacy. *q is the array. sizeof *q is the size of the array. _Generic(*q, ... does print "we ended up with an int*" though because *q is used in an expression? Interesting _Generic does that step.
@chux-ReinstateMonica It has to decay because it isn't possible to pointer arithmetic on arrays, only on pointers. Similarly, the array subscripting operator [] can actually not be used on arrays. See stackoverflow.com/questions/55747822/….
@Lundin when you say "It has to decay because ...", are you applying that to _Generic(*q,... which does not has pointer arithmetic on arrays?
|

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.