Search code examples
arrayscpointersprintfimplicit-conversion

Printing pointers itself in C


I am new to programming in C and I am trying to understand the concept of pointers. However, the result of some print statements are quite confusing and I was not able to find suitable answers online.

So, I was running the following main function in C:

int main() {
    int a[] = { 1, 2, 3, 4, 5 }; // a points to the address of a[0]
    int *p = a; // p points to the same address as a
    int **pp = &p; // points to p

    int *p2[] = { a, a+1, a+2, a+3, a+4 }; // points a
    int **pp2 = p2; // points to the same address as p2

    printf("  a: %p,         *a: %p\n", a, *a);
    printf("  p: %p,         *p: %p\n", p, *p); 
    printf(" pp: %p,        *pp: %p,        **pp: %p\n",        pp,  *pp,  **pp);
    printf(" p2: %p,        *p2: %p,        **p2: %p\n",        p2,  *p2,  **p2);
    printf("pp2: %p,       *pp2: %p,       **pp2: %p\n",       pp2, *pp2, **pp2);
    
    return 0;
}

With gcc main.c -o main and ./main I got the following output:

  a: 0061FF04,         *a: 00000001
  p: 0061FF04,         *p: 00000001
 pp: 0061FEEC,        *pp: 0061FF04,        **pp: 00000001
 p2: 0061FEF0,        *p2: 0061FF04,        **p2: 00000001
pp2: 0061FEF0,       *pp2: 0061FF04,       **pp2: 00000001

which indicates that both a and p point to a[0] and pp to p, which is expected behavior. It also makes sense that p2 and pp2 point to the same address as a, but I cannot think of a reason, why printing out a and p as well as p2 and pp2 results in the same address (0061FF04 and 0061FEF0, respectively). Any help is appreciated


Solution

  • For starters all your calls to printf have undefined behavior because you use an invalid conversion specifier p with objects of the type int. To output objects of the type int you need to use conversion specifier d or i.

    Arrays used in expressions with rare exceptions are implicitly converted to pointers to their first element.

    From the C Standard (6.3.2.1 Lvalues, arrays, and function designators)

    3 Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

    For example in this declaration

    int *p = a;
    

    the array a used as an initializing expression is implicitly converted to a pointer to its first element. You may equivalently rewrite the declaration the following way

    int *p = &a[0];
    

    Dereferencing the pointer p as in *p that is the same as *a or p[0] or a[0]: you will get the first element of the array a.

    So you should write

    printf("  a: %p,         *a: %d\n", ( void * )a, *a);
    printf("  p: %p,         *p: %d\n", ( void * )p, *p);
    

    In this declaration

    int *p2[] = {a, a+1, a+2, a+3, a+4}; 
    

    you define an array of pointers to elements of the array a.

    So for example the array designator p2 used in expressions is converted to a pointer to its first element.

    So the pointer pp2 declared as

    int **pp2 = p2;
    

    also receives the address of the first element of the array p2. That is the value of pp2 is equal to the value of p2 after implicit conversion of the array designator p2 to a pointer to its first element.

    On the other hand, expressions *pp2 and *p2, that are the same as pp2[0] and p2[0], yield the first element of the array p2. And expressions **pp2 and **p2, that are the same as pp2[0][0] and p2[0][0] (or ( *pp2 )[0], (*p2 )[0] or *pp2[0], *p2[0]), yield the first element of the array a because the first element of the array p2 contains the address of the first element of the array a.

    So you should write

    printf(" p2: %p,        *p2: %p,        **p2: %d\n",        ( void * )p2,  ( void * )*p2,  **p2);
    printf("pp2: %p,       *pp2: %p,       **pp2: %d\n",       ( void * )pp2, ( void * )*pp2, **pp2);
    

    Though values of the expressions pp2 and p2 are equal (they both have the value of the address of the first element of the array p2) the values of pp and pp2 are different because the pointer pp points to the pointer p. However the values of expressions *pp2 and *pp are equal because the both point to the first element of the array a.