Thread: Double pointers - C

  1. #1
    Registered User
    Join Date
    Jun 2019
    Posts
    33

    Double pointers - C

    Hi everybody,

    I'm learning C language (actually it's better to say that I'm trying to learn it), and I have some difficulties with the use of double pointers.

    I give you an example here:

    //BEGINNING OF THE CODE

    int a=5;
    int b=2;

    int *point;
    int **doublepointer;

    pointer=&a;
    /*FIRST QUESTION: now 'pointer' is equal to the adress of 'a', and '*pointer' is equal to the value of 'a' . Is that correct?*/

    doublepointer=*pointer;
    /*SECOND QUESTION, I'M NOT SURE ABOUT THIS: now 'doublepointer' is equal to the adress of 'pointer', and '*doublepointer' is equal to the value pointed by 'pointer'.
    Is that correct? I don't think so. Can anyone correct these sentences?*/

    b=*doublepointer;
    /*THIRD QUESTION, what happen if I do this?*/

    b=**doublepointer;
    /*FOURTH QUESTION, what happen if I do this?*/

    //TERMINATION OF THE CODE


    Sorry if some of these questions were pretty silly, but the use of double pointers is confusing me.
    I can't see the whole picture, how can I manage double pointers using one * or two **.

    Thanks everybody!
    Last edited by Salem; 06-24-2019 at 02:45 PM. Reason: Font size abuse

  2. #2
    Registered User
    Join Date
    May 2019
    Posts
    213
    Going backwards, assuming:

    Code:
    int a = 5;
    int b = 2;
    int *pointer;
    int **dpointer;
    
    There's nothing all that mysterious about dpointer. The only real difference in the concept between dpointer and pointer is that where pointer may point to the address of a variable, dpointer points to the address of a pointer. One could argue that dpointer is that singularly special case where the type it points to happens to be a pointer, whereas pointer does not.

    That is, where

    Code:
    pointer = &a 
    or
    pointer = &b
    This does the same kind of thing

    Code:
    dpointer = &pointer
    with the only real exception being that pointer is a pointer, not an integer (or whatever type the pointer is declared to point to).

    This means that whatever can be done with a or b can be done with *pointer:

    Code:
    //where 
    pointer = &a;
    
    a = 6;
    *pointer = 6;
    Likewise, whatever can be done with pointer can be done with *dpointer

    Code:
     //where 
    pointer = &a;
    dpointer = &pointer;
    
    *pointer = 8;   // which is the same as a = 8;
    **dpointer = 8; // same as *pointer = 8
    
    *(*dpointer) = 8; // think of it this way, where *dpointer is the same as pointer
    When used in assignments (not declarations), you can read "*pointer" as: what's stored at pointer.

    Now, to your questions:

    #1: Yes, a pointer stores the address of something, in this case an integer, such that the "&a" gives the pointer to where a is stored, and pointer = &a copies the address of a to the pointer.

    #2: You've written the statement incorrectly. dpointer can't accept *pointer, *pointer is read in this context as "what's stored at" pointer. dpointer is not declared to take that type (it is an integer), dpointer is declared to take the address of a pointer (a double pointer does that). The correct fashion for this example question should be dpointer = &pointer; meaning that dpointer, which is declared to accept the address of a pointer, is assigned to the address of pointer.

    #3 If we assume dpointer is correctly assigned to the address of a pointer, as in dpointer = &pointer, we have to qualify this with one important point: we must know that pointer is also valid to use this, so let's assume, for simplicity, that:

    Code:
    int a = 5;
    int b = 2;
    int *pointer;
    int **dpointer;
    
    pointer = &a; // pointer is now valid to use
    dpointer = &pointer; // dpointer is valid to use
    
    //b = *dpointer; // this is an error and won't compile (usually)
    b = **dpointer;
    b = *pointer;  // this is the same as **dpointer
    
    b is an integer. It can be assigned to another integer, copying the value, as in b = a;

    pointer points to an integer, and if assigned as above, what's stored at that pointer is an integer, so b = *pointer;

    dpointer points to the address of a pointer to an integer, so what's stored at dpointer is a pointer to an integer. Think of that this way:

    Code:
    b = * (*dpointer);
    Here, dpointer is 'dereferenced' inside the parenthesis. Since dpointer is a pointer to a pointer, what is stored at dpointer is a pointer.

    That isn't an integer, is a pointer to an integer. To get the integer, it must be dereferenced again, because what "comes out of" of the parenthesis is a pointer, it's just like "pointer", so

    Code:
    * (*dpointer);
    
    //is the same as
    
    *pointer;
    So,

    Code:
    int a = 5;
    int b = 4;
    
    int *pointer = &a;
    int **dpointer = &pointer;
    
    **dpointer = 4; // a is now 4
    
    if ( *pointer == b ) // is true
    Now, in closing, this is a crash:

    Code:
    int a = 5;
    int b = 4;
    
    int *pointer = NULL;
    int **dpointer = &pointer; // this is ok but be warned what's next
    
    **dpointer = 3; // this crashes because pointer itself is NULL.
    
    *dpointer = &a; // this does the same as pointer = &a
    
    **dpointer = 2; // this would work if it hadn't crashed above

  3. #3
    Registered User
    Join Date
    Feb 2019
    Posts
    555
    Pointer is a variable as any other. I like to explain this way:

    This is a DECLARATION of a variable p:
    Code:
    int *p;
    Here p will be used to hold the address where an int is, in memory, but it is uninitialized.

    When you do:
    Code:
    int x;;
    int *p;
    x = 5;
    p = &x;
    The variable p will hold the address-of where variable x is, in memory. And here is another concept: The USE of variables. The int *p, above, is the DECLARATION of a variable, where p = &x; is its USAGE. And you use variables through expressions... p = &x; is the expression "assign to p the address-of x".

    Of course, C compilers allow you to mix both declarations and usage in a single line, like:
    Code:
    int *p = &x;
    But this is the same as:
    Code:
    int *p;
    p = &x;
    Why differenciate between declarations and usage? Because that asterisk in int *p; is you telling the compiler that p will be used to hold an address an int. While, in usage, doing something like *p = 1; is you talking about an expression using 2 operators: * (for indirection) and = (for assignment). In this usage you are telling * (as indirection) is "access the data at the address inside the variable p"... Nothing to do with the * in the declaration of p...

    When you declare:
    Code:
    int **pp;
    You are telling the compiler that the variable pp is a pointer (i.e, will hold an address), but, when using pp, using a single * operator, you will get another address (not the int data) which, in turn, points to the data. So, if you declare:
    Code:
    int x = 5;
    int *p = &x;
    int **pp = &p;
    pp will hold the address of the variable p (which is a pointer). The expression *p will access memory where the data of x is, and the expression *pp will access the address where p is... **pp will access the data where x is as well, but just because pp points to p and p points to x.

    All this sounds very complicated, here's a practical example. Suppose you want to change an argument passed to a function, inside this function. You can do something like this:
    Code:
    void f(int *p) { (*p)++; }
    ...
    int x = 1;
    f(&x);
    // here x will be 2
    But, suppose you want to change an argument, which is a pointer! The principle is the same, you pass a pointer to a pointer! As in:
    Code:
    int reallocateMemory( void **pp, size_t size )
    {
      // tries to reallocate memory (could return NULL if error).
      void *p = realloc( *pp, size );
    
      // If everything is OK, change the pointer!
      if ( p )
        *pp = p;
    
      // Return a boolean informing the status of this operation (0 = error, 1 = ok).
      return p != NULL;
    }
    To use this:
    Code:
    int *p = NULL;
    
    if ( ! reallocateMemory( &p, 1000 ) )
    { ... error handling ... }
    Notice that &p will not be NULL (p is NULL, not the address of p!), we are getting the address-of p here.

    Well... I hope this helps...

    PS: Yeah, yeah... for the purists: I know there are some not-so-precise concepts, expressed here, accordingly to the ISO 9989 standard. My intent is to clarify concepts, not the standard...

  4. #4
    Registered User
    Join Date
    Jun 2019
    Posts
    33
    Thanks a lot Niccolo!!!
    I read everything, you've been very clear.
    I have two new questions, which were born while I read your answer.

    Question number 1
    When you wrote:
    Quote Originally Posted by Niccolo View Post
    Now, in closing, this is a crash:

    Code:
    int a = 5;
    int b = 4;
    
    int *pointer = NULL;
    int **dpointer = &pointer; // this is ok but be warned what's next
    
    **dpointer = 3; // this crashes because pointer itself is NULL.
    
    *dpointer = &a; // this does the same as pointer = &a
    
    **dpointer = 2; // this would work if it hadn't crashed above
    Why is it wrong to write
    **dpointer=3;
    ?
    I guess it is a legal expression, because **dpointer is declared as pointer to a pointer to a int.
    So, why does it crash?

    Question number 2:

    You explained that if I have

    Code:
    int b=5;
    int **dpointer;
    It is not legal to write
    Code:
    *dpointer=b;
    because dpointer is a pointer to a pointer (declaration), so it has not sense to write that.
    BUT, what if I consider a pointer to a record?

    I give you an example immediately.

    Let's consider
    Code:
    typedef struct list{ //I create a new kind of data
    
    
        float value;
        struct list *next_ptr;
        }lista;
    Now let's assume that I created some of these records (with malloc function), and I want to point with a doublepointer to the data the record I created.

    Code:
    float d=5;
    list *pointer;
    list **doublepointer; //I declare a pointer to this record
    (*doublepointer)->value=d;
    (*doublepointer)->next_ptr=pointer;
    Why in this case is it correct to write
    (*doublepointer)->value=d;
    ?
    Last edited by letthem; 06-24-2019 at 02:18 PM.

  5. #5
    Registered User
    Join Date
    Jun 2019
    Posts
    33
    Quote Originally Posted by flp1969 View Post
    Well... I hope this helps...
    It does flp1969, thank you!!!
    If you want, take a look to the answer I gave to Niccolo

  6. #6
    Registered User
    Join Date
    Feb 2019
    Posts
    555
    Quote Originally Posted by letthem View Post
    It does flp1969, thank you!!!
    If you want, take a look to the answer I gave to Niccolo
    Notice, in Nicolo example, pointer is initialized with NULL (0). Any indirection to a NULL address will crash (segmentation fault).
    dpointer holds the address of pointer, and, if pointer is initialized with NULL, *dpointer will give you NULL, and **dpointer will try to access a NULL address...

    I think you're having some dificulty grasping the concept of a pointer... Again: A pointer variable is a "normal" variable which holds an memory address. It exists to be used with * operator (or ->, in case of pointers to structures). If you don't initialize the variable, the address which is pointed is "undefined" and probably will get you a segmentation fault...

  7. #7
    Registered User
    Join Date
    Feb 2019
    Posts
    555
    I recommend (seriosly, not a joke) you draw, like this:

    In the figure code 1 is:
    Code:
    int x = 5;
    int *p = &x;
    int **pp = &p;
    And code 2 is:
    Code:
    int x = 5;
    int *p = &x;
    int **pp;
    Double pointers - C-untitled-png

    When you have *p, in an expression, you are getting the contents pointed by p (the contents of x)... *pp you'll get contents of p... and **pp you'll get the contents of x (like in *(*p)).

    But, in code 2, pp isn't properly initialized, so **pp will crash your program.

    Notice: There is no "double pointer", but double "indirection"... A pointer always points to a single object in memory. But it can point to another pointer...
    Last edited by flp1969; 06-24-2019 at 03:55 PM.

  8. #8
    Registered User
    Join Date
    May 2019
    Posts
    213
    I can't do better than @flp1969 at question 1. My point in presenting the illustration was to get you thinking about two separate levels implied by the use of such pointers. There is the compile time level, which you're asking about. Then, there's the run time level, which this example introduced to you. Indeed, the expression **dpointer = 3 is legal and fine at compile time, but the compiler isn't "thinking" about run time. It so happens that at that point no storage has been configured for that integer, because *dpointer is NULL (0).

    As to this:

    Code:
    float d=5;
    
    list *pointer;
    
    list **doublepointer; //I declare a pointer to this record
    
    (*doublepointer)->value=d;
    
    (*doublepointer)->next_ptr=pointer;
    In this exact code, no storage has been arranged. It is left uninitialized, though your comment hints you might have assigned something not written, I can't say that's true.

    However, the compile time formation of "(*doublepointer)->value" is a double de-reference. This is because (*doublepointer) is a pointer to a list, like pointer. If pointer were assigned to a correct memory location for a list instance, "pointer->value" is a way of writing "the value member of the list pointer points to". Put another way, it's like "(*pointer).value". To that end, if doublepointer is pointing to a valid "pointer", itself properly pointing to an instance of "list", then "(**doublepointer).value" is the same as "(*pointer).value", which is the same as (*doublepointer)->value which is the same as "pointer->value".

    The "->" operator is a dereference to member. (*pointer) is a dereference of the pointer (which is, then, the instance). If "list l;" instantiated a list as "l", and pointer = &l, and doublepointer = &pointer, then l.value is the same as (*pointer).value and (**doublepointer).value and pointer->value and (*doublepointer)->value.
    Last edited by Niccolo; 06-24-2019 at 04:02 PM.

  9. #9
    Registered User
    Join Date
    Jun 2019
    Posts
    33
    Quote Originally Posted by flp1969 View Post

    Notice: There is no "double pointer", but double "indirection"... A pointer always points to a single object in memory. But it can point to another pointer...
    Thank you flp1969, you've been very clear!!

  10. #10
    Registered User
    Join Date
    Jun 2019
    Posts
    33
    Quote Originally Posted by Niccolo View Post

    However, the compile time formation of "(*doublepointer)->value" is a double de-reference. This is because (*doublepointer) is a pointer to a list, like pointer. If pointer were assigned to a correct memory location for a list instance, "pointer->value" is a way of writing "the value member of the list pointer points to". Put another way, it's like "(*pointer).value". To that end, if doublepointer is pointing to a valid "pointer", itself properly pointing to an instance of "list", then "(**doublepointer).value" is the same as "(*pointer).value", which is the same as (*doublepointer)->value which is the same as "pointer->value".
    Great Niccolo, I was confused about the use of pointers, double pointers and structures.
    Thanks to you, flp1969 and the immortal "Kernighan Ritchie", I've understood what was missing!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Why I should be use double pointers?
    By Jacopo in forum C Programming
    Replies: 5
    Last Post: 03-11-2018, 06:19 PM
  2. Replies: 4
    Last Post: 08-29-2015, 01:15 PM
  3. double pointers
    By lexitron in forum C Programming
    Replies: 2
    Last Post: 11-09-2011, 01:40 AM
  4. double pointers
    By esi in forum C Programming
    Replies: 1
    Last Post: 04-14-2007, 04:06 PM
  5. double pointers?
    By Kohatian 3279 in forum C++ Programming
    Replies: 1
    Last Post: 03-17-2002, 04:16 AM

Tags for this Thread