-1
void test(int *p2) {
    *p2 = 3;}

int main()
{

int* p1, x = 5;

p1 = &x;
test(p1); // p1 and p2 are pointing to the same address which is x's address
printf("%d", x); //prints 3    

this example 2 pointers pointing to the same address, which passing to the function by reference.

Now take this 2nd example

void test(int **p2) {
    **p2 = 3;
}
int main()
{

    int* p1, x = 5;

    p1 = &x;
    test(&p1); // p2 is pointing to p1 address
    printf("%d", x);

so are double pointers necessary in these type of situations? especially with structured linked lists?

typedef struct NOde {
    int data;
    struct NOde* next;
}node;
void test(node *head) {
    node* new_node = (node*)malloc(sizeof(node));
    new_node->data = 5;
    new_node->next = head; 
    head= new_node; 
}
int main()
{
    node* head=NULL;

    test(head);

and why in this one, the head values in the main still NULL if it same concept as above?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • The more typical use in the second example is `*p2 = malloc(sizeof node);`. That is, it allows the function to return a pointer value back to the caller. – kaylum Dec 13 '19 at 09:57
  • 6
    But your question is not that well defined. It's like asking, is a hammer necessary? Well there is a reason why a hammer was invented so it is useful if used for the right reasons and not useful when it's not the appropriate tool for the job at hand. – kaylum Dec 13 '19 at 10:00
  • 1
    "same concept as above". It's not the same concept. In the first example you dereferenced the passed in pointer for the assignment. Where in your last example do you dereference `head`? Nowhere. And hence the caller's pointer is not changed. – kaylum Dec 13 '19 at 10:08
  • @kaylum if all you have is a hammer, everything looks like a nail – Ctx Dec 13 '19 at 10:08
  • @kaylum so will it work if i dereference the head? as *head=*new_node ? – Mustafa Shama Dec 13 '19 at 10:12
  • If you want to modify an `int` parameter from a function, you need to pass a _pointer to int_ (`int*`) to the function. If you want to modify an `int*` parameter from a function, you need to pass a _pointer to a pointer to int_ (`int**`) to the function. Also read this: https://stackoverflow.com/questions/766893/how-do-i-modify-a-pointer-that-has-been-passed-into-a-function-in-c – Jabberwocky Dec 13 '19 at 10:13
  • Why don't you try it? The answer is no as that will likely crash (technically it's undefined behaviour). Because `head` is NULL and thus can't be dereferenced. – kaylum Dec 13 '19 at 10:13
  • 1
    Does this answer your question? [changing pointers in functions, c](https://stackoverflow.com/questions/7545326/changing-pointers-in-functions-c) – kaylum Dec 13 '19 at 10:13
  • There are many book as well as questions on SO which deal with pointers. Suggest you read some of those including the one above. – kaylum Dec 13 '19 at 10:14
  • @MustafaShama Your code seems to be C, not C++. Please clarify which language you are using/asking about. – Konrad Rudolph Dec 13 '19 at 11:23

8 Answers8

2

Pointers (*p) are sufficient when you want to change the contents of the address the pointer is pointing at.

Double star pointers (**p) are necessary when you want to change the address the pointer is pointing at.

In the following code, inspect the outcome of the second printf statements especially.

#include <stdio.h>
#include <stdlib.h>

void swapValues(int *p, int val) {
    *p = val;
}

void swapPointers(int **p, int *val) {
    *p = val;
}

int main() {
    int x, y;
    int *p1 = &x;
    int *p2 = &x;

    x = 3;
    y = 5;
    printf("x = %d y = %d p1 = %d p2 = %d\n", x, y, *p1, *p2);
    printf("p1 = %p p2 = %p\n", p1, p2);

    swapValues(p1, y);
    printf("x = %d y = %d p1 = %d p2 = %d\n", x, y, *p1, *p2);
    printf("p1 = %p p2 = %p\n", p1, p2);

    x = 3;
    y = 5;
    swapPointers(&p2, &y);
    printf("x = %d y = %d p1 = %d p2 = %d\n", x, y, *p1, *p2);
    printf("p1 = %p p2 = %p\n", p1, p2); // observe value of p2 here

    return 0;
}
ssd
  • 2,340
  • 5
  • 19
  • 37
1

In C, all function calls are made by value. Which essentially means that the called function always gets its own copy of the arguments you pass to it. Same goes with the value you return from the function. There is always a copy of this value given back to the caller. The moment a function finishes execution, all arguments passed to it and local variables declared within it cease to exist.

For example:

int add(int a, int b)
{
    int result = a + b;
    return result;
}

int main()
{   
    int p = 3, q = 5;
    int r = add(p,q);
}

In this case, a and b are copies of p and q respectively, and r is a copy of result. p, q and result no longer exist after add() has finished execution.

Now, this is fine for many common use-cases as in the example above. But what if you want to change the value of one of the variables in the calling function from within the called function? You then need to pass the address of the variable, so that the called function can indirectly access the variable in the calling function and update it.

Example:

void inc(int *p)
{
    *p = *p + 1;
}

int main()
{   
    int a = 5;
    inc(&a);
}

In this case, the called function gets a copy of the address of a, called p, using which it is able to update the memory location holding a indirectly. This is called dereferencing a pointer.

Now, to address your question, we need to take this one step further - what if we need to update a pointer in the calling function? We need to pass a pointer to the pointer - also called a double pointer.

In your example, we need to update head, which is already a pointer to a Node. So we need to pass the address of head, for which we need a double pointer.

Hence your code should be:

void test(node **phead) 
{
    node* new_node = (node*)malloc(sizeof(node));
    new_node->data = 5;
    new_node->next = *phead;
    /* Note the dereferencing here - we update `head` indirectly through a pointer */
    *phead = new_node; 
}

test(&head);

Otherwise, we would be passing around a copy of head, which is a pointer, using which you can access the node that head points to, but not head itself. If you increment this pointer within your function, the change is not reflected outside, because this copy ceases to exist the moment the function returns.

PS: C++, unlike C, supports call by reference, which means the language transparently handles the pointer management and lets you directly update variables passed to you 'by reference'.

th33lf
  • 2,177
  • 11
  • 15
0

In your case no, because to assign the value you only need one pointer.

void test(int *p2) {
    *p2 = 3;
}

Pointers to pointers are useful when you want to change the pointer.

A common use of pointers to pointers is methods that create something, but want to return something other than the pointer itself, e.g.

myerror_t create_foo(foo_t **p, int a, int b, int c)
{
    if (a < 0 || b < c) return MYERR_INVALID_ARG;
    *p = malloc(sizeof foo_t);
    p->x = a * b  * c;
    return MYERR_SUCCESS;
}

Note that in C++, sometimes references are used when changing the value, and they can function in a very similar way.

void test(int &p2) {
    p2 = 3;
}

Also note in C++, that throwing an exception, often from a constructor, is more common that a create_foo style method.

Foo::Foo(int a, int b, int c)
{
    if (a < 0) throw std::invalid_argument("Foo a < 0");
    if (b < c) throw std::invalid_argument("Foo b < c");
    x = a * b  * c;
}

If a factory function is desired, it might return the pointer and throw exceptions.

Fire Lancer
  • 29,364
  • 31
  • 116
  • 182
0

double pointers are needed if you are going to change the pointer itself in the function

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int value1 = 10;
int value2 = 20;


void choose(int x, int **pointerToValue)
{
    switch(x)
    {
        case 0:
            *pointerToValue = &value1;
            break;
        case 1:
            *pointerToValue = &value2;
            break;
        default:
            *pointerToValue = NULL;
    }
}

int main()
{
    int *ptr;
    int choice;

    choose(choice = rand() & 1, &ptr); 
    //ptr value was changed in the function

    printf("Choice = %d, value = %d\n", choice, ptr ? *ptr : 0);  
}
0___________
  • 60,014
  • 4
  • 34
  • 74
0

Are double pointers necessary?

TL;DR:

The moment a function shall change the value of a pointer defined by the calling function, then yes, they are (and can even become triple, quatuple, ... pointers).

Longish answer:

To have a function change a variable of type T and this variable is defined by the caller, the caller needs to pass to the function a pointer to T, that is a T*.

The function then needs to dereference the T* variable using the * operator as shown in your 1st snippet:

void test(int *p2) {
  *p2 = 3; /* p2 is dereferenced, so the assignment works on the variable it is pointing to. */
}

So if then T already is a pointer type then T* would be a pointer to a pointer.

This latter case should be the appearing within the last snippet you show. And it shall be handled exactly as in the 1st snippet. node * head shall be changed within test2(). So pass the address of head;

  test2(&head);

To be able to do so the parameter of test2 need to be defined as a pointer to the type of head. head is node*, so a pointer to is is node**.

void test2(node ** phead)

To then change the value of the caller's head inside the function the pointer to head, namely phead needs be dereferenced:

*phead = ....    

why in this one, the head values in the main still NULL if it same concept as above?"

Comparing the lines of your last snippet to the versions in my answer, you see that your code in fact is not using the "same concept" but misses a level of indirection, which your 1st snippet indeed uses ...;)

alk
  • 69,737
  • 10
  • 105
  • 255
0

You would only pass a double (or triple, or whatever) pointer to a function if you need the function to write a new pointer value to the parameter, or if you're dealing with multiple-dimensioned data (an array of pointers to arrays (of pointers to arrays of ...)).

If you want a function to write to a parameter of type T, then you need to pass a pointer to T:

void foo( T *p )
{
  *p = new_T_value(); // write a new value to the thing p points to
}

void bar( void )
{
  T var;
  foo( &var ); // foo writes a new value to var
}

Now let's replace T with the pointer type P *:

void foo( P **ptr )
{
  *ptr = new_pointer_to_P_value;
}

void bar( void )
{
  P *var;
 foo( &var ); // write a new pointer value to var
}

Once more for emphasis, replace P with Q *:

void foo( Q ***ptr )
{
  *ptr = new_pointer_to_pointer_to_Q_value;
}

void bar( void )
{
  Q **var;
  foo( &var ); // write a new value to var
}

The semantics are the same in all three cases, all that's changed is the level of indirection. For N levels of indirection in bar, you need N+1 levels of indirection in foo.

The other case for multiple indirection is multiple-dimensioned data (arrays of pointers to arrays of pointers to ...). For example:

void create_2d_arr( int ***arr, size_t rows, size_t cols )
{
  *arr = malloc( rows * sizeof *(*arr) );
  if ( *arr )
  {
    for ( size_t i = 0; i < rows; i++ )
    {
      (*arr)[i] = malloc( cols * sizeof *(*arr)[i] )
      {
        for ( size_t j = 0; j < cols; j++ )
        {
          (*arr)[i][j] = initial_value;
        }
      }
    }
  }
}

As to your example:

void test(node *head) {
    node* new_node = (node*)malloc(sizeof(node));
    new_node->data = 5;
    new_node->next = head; 
    head= new_node; 

Yes, in this case, if you want the change to head to be seen in main, you have to pass a pointer to the pointer:

void test(node **head) {
    node* new_node = (node*)malloc(sizeof(node));
    new_node->data = 5;
    new_node->next = *head; 
    *head= new_node; 
}

int main( void )
{
  ...
  test( &head );
  ...
}
John Bode
  • 119,563
  • 19
  • 122
  • 198
-1

It's not necessary double pointer because p2 is in the scope of the function so in the main function is not visibile. And you don't want to change the pointer address but only the value of the pointed variable.

Zig Razor
  • 3,381
  • 2
  • 15
  • 35
-1

If ypu want to change a variable in a function you should pass it by reference in C or C++ meaning.

Consider your example of a singly-linked list. The variable head has a pointer type.

node* head=NULL;

So to change it in the function test you need to pass the variable by reference. For example

A C implementation passing by reference

void test( node **head, int data ) 
{
    node *new_node = ( node* )malloc( sizeof( node ) );
    new_node->data = data;
    new_node->next = *head; 
    *head=   new_node; 
}

and a C++ implementation passing by reference

void test( node * &head, int data ) 
{
    head = new node { data, head };
}

Without passing the head node by reference in the C function implementation the function deals with a copy of the value stored in head. You can imagine the function and its call the following way

test( head, 5 );
//...

void test( /*node *parm_head, int data*/ ) 
{
    node *parm_head = head;
    int data = 5;
    node *new_node = ( node* )malloc( sizeof( node ) );
    new_node->data = data;
    new_node->next = *head; 
    *head=   new_node; 
}

That is function parameters are its local variables.

Consider another example when you need ay first to allocate an array to pointers to strings. For example

char **strings = malloc( 10 * sizeof( char * ) );
for ( size_t i = 0; i < 10; i++ )
{
    strings[i] = malloc( 100 );
} 

Now if you want to reallocate the original array by adding one more string then you gave to pass a pointer to this double pointer. For example

void add_one_more_string( char ***strings )
{
    char **tmp = realloc( *strings, 11 ( sizeof( char * ) );

    if ( tmp != NULL ) *stringvs = tmp;

    //…
} 

and call the function like

add_one_more_string( &strings );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • The question was specifically about **C**, not C++. You can’t just edit the question to add another language tag. – Konrad Rudolph Dec 13 '19 at 10:48
  • @KonradRudolph Initially the question had also the tag C++. Somebody removed it. Do not change the tags. – Vlad from Moscow Dec 13 '19 at 11:16
  • We have a long-standing policy *against* double-tagging questions as C and C++ unless there’s a specific issue necessitating both of them. Max was correct in applying this policy, and you were wrong to revert it. – Konrad Rudolph Dec 13 '19 at 11:22
  • @KonradRudolph Passing by reference is a common question in C and C++. – Vlad from Moscow Dec 13 '19 at 11:23
  • No it isn’t, because it doesn’t exist in C. We can have separate questions with separate answers for C and C++. – Konrad Rudolph Dec 13 '19 at 11:25
  • @KonradRudolph: “Reference” is a word used to describe passing the address of an object. It was used in this way before C++ made it a built-in feature of the language. It C, a reference to an object can be passed manually, rather than as a built-in feature, by passing a pointer. The fact that C++ used “reference” as a name for a feature built into the language does not revoke the use of “reference” in other circumstances. The quote from the C standard that Vlad from Moscow provided illustrates that the C standard uses “reference” in this way—a pointer to an object is a reference to the object. – Eric Postpischil Dec 13 '19 at 14:04
  • @EricPostpischil I don’t disagree with anything you’re saying. You’re missing context though, because this discussion contained two more now deleted comments. The debate was specifically about a “pass by reference” mechanism. By contrast the quoted C standard passage is just a general allusion to something (= a pointer) referring to an object, and my objection is that this does not somehow imply that double-tagging this question is appropriate. For what it’s worth I don’t disagree with this answer (except for its unreserved use of the “pass by reference” terminology), nor did I downvote it. – Konrad Rudolph Dec 13 '19 at 14:29