2

can someone explain to me how double pointers work? On this code for example, why it prints the values that it prints in the end?

int main(void) {
    int anArray[] = {5,16,33,99};
    int * p = anArray;

    printf("*p = %d\n", *p);
    p++;
    printf("Now *p = %d\n", *p);

    int * q = &anArray[3];
    int ** x = &q;
    **x = 12;
    *x = p;
    **x = 42;
    q[1] = 9;

    for (int i =0; i < 4; i++){
        printf("anArray[%d] = %d\n",i, anArray[i]);
    }

    return EXIT_SUCCESS;
}
dbush
  • 205,898
  • 23
  • 218
  • 273
Vladushka
  • 25
  • 6
  • Draw it out; some people understand diagrams better. – Thomas Matthews Jul 18 '22 at 16:48
  • 1
    "Double pointer" is just a pointer to a pointer. Carefully go through each `&` and `*` and see what the result/type is and what it refers to. – Eugene Sh. Jul 18 '22 at 16:48
  • A pointer `T*` points at an object of type `T`. You can use it to manipulate the object that it points at. If `T` happens to be a pointer, then `T*` is a pointer to a pointer. But there's no magic there. You use that `T*` to manipulate the object that it points at. – Pete Becker Jul 18 '22 at 16:50
  • Your question is about a pointer to pointer. A double pointer is a pointer that can hold the address of a double. `int **p` declares a pointer to pointer. `double *p` declares a double pointer. – William Pursell Jul 18 '22 at 16:53

4 Answers4

5

Let's draw out:

int q = 15;
int * p = &q;
int * * z = &p;

            q   
          +----+  
    p --> | 15 |  
    ^     +----+  
    |  
    z  

In the above drawing, q is an integer variable with the value 15.
The pointer p points to the variable q.
The pointer z points to the pointer p.

The expression *z refers to the value in the pointer p.
The expression **z or rewritten as *(*z) refers to the variable q (following the links).

We know that (*z) is the pointer p, so let's replace:
*(*z) == *(p);

We can replace *(p) with q, from the above definitions: *(*z) == *(p) == q;.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
2

Let's fully draw it out:

int anArray[] = {5,16,33,99};  // 4 values in consecutive addresses

//            ----
// anArray ->   5
//            ----
//             16
//            ----
//             33
//            ----
//             99
//            ----

int * p = anArray; // A new variable, whose contents points to the address of anArray

//            ---------
// anArray ->   5       <-+
//            ---------   |
//             16         |
//            ---------   |
//             33         |
//            ---------   |
//             99         |
//            ---------   |
//       p ->  anArray   -+
//            ---------

printf("*p = %d\n", *p);  // prints 5
p++;                      // increment pointer p to the next element.

//            -----------
// anArray ->   5      
//            -----------
//             16         <-+
//            -----------   |
//             33           |
//            -----------   |
//             99           |
//            -----------   |
//       p ->  anArray+1   -+
//            ---------

printf("Now *p = %d\n", *p); // prints 16
int * q = &anArray[3];       // new pointer variable, points to 4th element of anArray (0-based)

//            -----------
// anArray ->   5      
//            -----------
//             16         <-+
//            -----------   |
//             33           |
//            -----------   |
//             99           | <-+
//            -----------   |   |
//       p ->  anArray+1   -+   |
//            -----------       |
//       q ->  anArray+3   -----+
//            -----------

int ** x = &q;  // new pointer variable, points to address of q pointer variable

//            -----------
// anArray ->   5      
//            -----------
//             16         <-+
//            -----------   |
//             33           |
//            -----------   |
//             99           | <-+
//            -----------   |   |
//       p ->  anArray+1   -+   |
//            -----------       |
//       q ->  anArray+3   -----+ <-+
//            -----------           |
//       x ->  q           ---------+
//            -----------

**x = 12;  // get address in x (q), then address in q (anArray+3) and write 12 to it

//            -----------
// anArray ->   5      
//            -----------
//             16         <-+
//            -----------   |
//             33           |
//            -----------   |
//             12           | <-+
//            -----------   |   |
//       p ->  anArray+1   -+   |
//            -----------       |
//       q ->  anArray+3   -----+ <-+
//            -----------           |
//       x ->  q           ---------+
//            -----------

*x = p;  // get address in x (q) and write p value to it (anArray+1)

//            -----------
// anArray ->   5      
//            -----------
//             16         <-+ <-+
//            -----------   |   |
//             33           |   |
//            -----------   |   |
//             12           |   |
//            -----------   |   |
//       p ->  anArray+1   -+   |
//            -----------       |
//       q ->  anArray+1   -----+ <-+
//            -----------           |
//       x ->  q           ---------+
//            -----------

**x = 42;  // get address in x (q), then address in q (anArray+1) and write a 42 to it

//            -----------
// anArray ->   5
//            -----------
//             42         <-+ <-+
//            -----------   |   |
//             33           |   |
//            -----------   |   |
//             12           |   |
//            -----------   |   |
//       p ->  anArray+1   -+   |
//            -----------       |
//       q ->  anArray+1   -----+ <-+
//            -----------           |
//       x ->  q           ---------+
//            -----------

q[1] = 9;  // get address in q (anArray+1) add one element (anArray+2), and assign 9

//            -----------
// anArray ->   5      
//            -----------
//             42         <-+ <-+   (q[0])
//            -----------   |   |
//              9           |   |   (q[1])
//            -----------   |   |
//             12           |   |
//            -----------   |   |
//       p ->  anArray+1   -+   |
//            -----------       |
//       q ->  anArray+1   -----+ <-+
//            -----------           |
//       x ->  q           ---------+
//            -----------
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
1

First of all, I will use a simple example to demonstrate double pointers:

int x = 5; // Simply 5
int* y = &x; // y holds the address of x
int** z = &y; // z holds the address of y

When y holds the address of x it means that no matter what is the value of x, y still points to it (points means 'holding the address of'). So, changing x also changes *y which is the syntax for accessing the value that y points to. The same is applied to z, but here we need to use * operator twice in order to access x value, because z points to y which points to x (meaning **z == *y == x).

To understand your question we also need to understand what arrays are in C. Array is a sequence of elements (e.g. integers, chars and etc...) in a contiguous memory location. An array is actually a pointer meaning if we have int anArray[] = {5,16,33,99}; accessing anArray using anArray[0] is the same as *anArray, or anArray[1] is the same as *(anArray+sizeof(int)) and etc...

shahar
  • 355
  • 2
  • 18
0

A pointer is a variable holding a memory address which contains the value you want. For example:

int value = 42;
int* value_ptr = &value;
int access_via_ptr = *value_ptr;

A double pointer, or a pointer to pointer, is a variable holding an address of another pointer, which holds an address to the value you want. For example:

int value = 42;
int* value_ptr = &value;
int** value_ptr = &value_ptr;
int access_via_ptr_ptr = **value_ptr;

Why would you want to use **? One really useful way is in functions. You want to pass a pointer to a pointer, so that the function can setup the pointer address for you. For example:

#include <iostream>

void GetPtr(int** val_ptr_ptr, int init_contents)
{
    // val_ptr_ptr should point to a pointer
    // lets allocate it
    *val_ptr_ptr = new int;

    // de-reference ptr->ptr->val to initialize
    // the contents
    **val_ptr_ptr = init_contents;
}

int main(void)
{
    int* val_ptr = NULL;
    GetPtr(&val_ptr, 42);
    std::cout << *val_ptr;
    delete val_ptr;
    return 0;
}

In this example we want a function to allocate memory. We want that memory to be stored in a pointer outside the function, so we can access it. We pass in an address of outside pointer, or a pointer to a pointer. We then assign the address inside the function.

Chris
  • 2,655
  • 2
  • 18
  • 22