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?

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
Talete
  • 81
  • 6
  • An array and its first element have the same address, but the type of corresponding pointers is different. – n. m. could be an AI Aug 17 '22 at 08: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... – Fe2O3 Aug 17 '22 at 09: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`. – Bob__ Aug 17 '22 at 09: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[ ]`... – Fe2O3 Aug 17 '22 at 09:13
  • @Lundin yes, you are right. Thank you for the correction – Josh Kuber Aug 17 '22 at 09:33

2 Answers2

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]
programmer
  • 462
  • 1
  • 6
  • "*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`. – chux - Reinstate Monica Aug 17 '22 at 17:41
  • 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]```. – programmer Aug 17 '22 at 17:55
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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 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 Aug 17 '22 at 10:56
  • @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. – Lundin Aug 17 '22 at 11:02
  • "`*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 - Reinstate Monica Aug 17 '22 at 17:42
  • @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 https://stackoverflow.com/questions/55747822/do-pointers-support-array-style-indexing. – Lundin Aug 18 '22 at 06:27
  • @Lundin when you say "It has to decay because ...", are you applying that to `_Generic(*q,...` which does not has pointer arithmetic on arrays? – chux - Reinstate Monica Aug 18 '22 at 06:32
  • @chux-ReinstateMonica _Generic is an expression and it's neither sizeof nor & so why wouldn't the array decay? – Lundin Aug 18 '22 at 06:40
  • Because it was not clear to me that `_Generic` is an expression. Given your assertion, that it is, then it well addresses the [comment](https://stackoverflow.com/questions/73385442/question-regarding-output-of-printf-in-handling-pointer-to-array/73385692?noredirect=1#comment129610506_73385692). – chux - Reinstate Monica Aug 18 '22 at 06:43