Search code examples
cpointerstreedouble-pointer

what is the difference between *root and **root?


I was iterating a tree data structure which has a pointer to its root as follows-

struct node *root;

when I have to pass reference of this root as a parameter to a function.... I have to pass it like-

calcHeight(&root);
-
-
-
//somewhere

int calcHeight(struct node **root)      // function defination is this

My question is- why do we need to pass "root" pointer as &root? can't we just pass root like--

struct node *root;
calcHeight(root);
int calcHeight(struct node *root);

// EDIT

void someFunct(int *arr){
    printf("arr2 inside someFunct is %d\n",arr[2]);
    arr[2]=30;
}

int main()
{
    int *arr=(int*)calloc(10,sizeof(int));
    printf("arr[2] is %d\n",arr[2]);

    someFunct(arr);
    printf("arr[2] finally is %d\n",arr[2]);
    return 0;
}

In this case arr in main function is modified even when I'm not passing the address of arr. I'm getting the fact that for structures and single value vars we HAVE to pass the address like someFunct(&var) but this is not necessary for arrays? for arrays we write someFunct(arr)
But I'm not getting the reason behind this?


Solution

  • struct node * is a pointer to a struct node.
    struct node ** is a pointer to a pointer to a struct node.

    The reason for passing in a struct node ** could be that the function needs to modify what the struct node * is actually pointing at - which seems odd for a function named calcHeight. Had it been freeNode it could have made sense. Example:

    void freeNode(struct node **headp) {
        free(*headp);
        *headp = NULL; // make the struct node * passed in point at NULL
    }
    

    Demo

    Another reason could be to make the interface consistent so that one always needs to supply a struct node ** to all functions in the functions supporting struct nodes - not only those actually needing to change what the struct node * is pointing at.


    Regarding the added // EDIT part:

    In this scenario there is no reason to send in a pointer-to-pointer. If you do not need to change the actual pointer, you only need to send in the value of the pointer.

    Example memory layout:

    Address    What's stored there
    +-----+
    |  +0 |     uint64_t   ui1 = 1     <--+
    +-----+                               |
    |  +8 |     uint64_t   ui2 = 2        |
    +-----+                               |
    | +16 |     uint64_t*  p   = &ui1  ---+
    +-----+
    

    Now, if a function only need an uint64_t value, you can send in ui1, ui2 or *p to that function.

    void someFunc(uint64_t val) { ++val; ... }
    

    The changes this function makes to val are not visible to the caller of the function.

    If a function is supposed to be able to make changes that are visible to the caller of the function, send in a pointer:

    void someFunc(uint64_t *valp) { *valp = 10; }
    

    Calling it with someFunc(&ui1); or someFunc(p); will change ui1 and assign 10 to it.

    If you have a pointer and want to change what it's actually pointing at, which is what your original question was asking, you would need to send in a pointer to that pointer:

    void someFunc(uint64_t **valpp) { *valpp = &ui2 }`
    

    If you call that with someFunc(&p) (where p is currently pointing at ui1) you will find that after the function call, p will point at ui2:

    +-----+
    |  +0 |     uint64_t   ui1 = 1
    +-----+
    |  +8 |     uint64_t   ui2 = 2     <--+
    +-----+                               |
    | +16 |     uint64_t*  p   = &ui2  ---+
    +-----+