Thread: Why do you need pointers to pointers?

  1. #1
    Registered User
    Join Date
    Apr 2010
    Location
    Vancouver
    Posts
    132

    Why do you need pointers to pointers?

    I'm having trouble understanding why pointers to pointers arise and am hoping someone could give me a very high level general explanation. For example
    int **myVar

    One thing I don't understand is int *myVar could be an array of int's - ok. But I don't see how if follows that int **myVar is an array of array's of int's because myVar still just points to a single address in memory. No matter how many * it always is just one address.

  2. #2
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    One example would be a pointer to an array of pointers. I've used this when dealing with text files, using the array of pointers to point to each record (variable length) of a text file that has been read into memory. I needed a pointer to the array of pointers because I allocated them.

    Another example would be when a function needs to be able to update a pointer, but in the case of C++, you could use a reference to the pointer instead.
    Last edited by rcgldr; 05-20-2013 at 03:16 AM.

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by c_weed View Post
    I'm having trouble understanding why pointers to pointers arise and am hoping someone could give me a very high level general explanation. For example
    int **myVar

    One thing I don't understand is int *myVar could be an array of int's - ok. But I don't see how if follows that int **myVar is an array of array's of int's because myVar still just points to a single address in memory. No matter how many * it always is just one address.
    Firstly, a pointer is a variable with a value, not an array. Like any variable it has an address. If you want the address of a pointer, you need a pointer to a pointer.

    Second "int **myVar" is not an "array of arrays of ints". The notional syntactic equivalence between pointers and arrays only goes so far (since it is not always so). It definitely breaks down as soon as you start talking about "pointers to pointers" and "arrays of arrays" --- as those things are not equivalent.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  4. #4
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    here's an example of where you might need a pointer to a pointer:

    Code:
    #include <iostream>
    
    class Base
    {
      
    };
    
    class Foo : public Base
    {
      public:
        virtual ~Foo() {}
    };
    
    class Bar : public Base
    {
      public:
        virtual ~Bar() {}
    };
    
    bool Factory(const std::string& className, Base **base)
    {
      if (className == "Foo")
      {
        *base = new Foo();
        return true;
      }
      else if (className = "Bar")
      {
        *base = new Bar();
        return true;
      }
      else
      {
        *base = nullptr;
        return false;
      }
    }
    
    int main()
    {
      Base* base = nullptr;
      
      if (Factory("Foo", &base))
      {
        std::cout << "Success" << std::endl;
      }
      else
      {
        std::cout << "Fail" << std::endl;
      }
      
      delete base;
      
      return 0;
    }
    this isn't the best example, because Factory() could simply return the pointer, but it gives you an idea of why you'd use a pointer to a pointer, not as an array.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  5. #5
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Elkvis View Post
    this isn't the best example, because Factory() could simply return the pointer, but it gives you an idea of why you'd use a pointer to a pointer, not as an array.
    It's as good an example as any. There are very few problems for which there is only one feasible solution in C++.

    The choice among feasible solution approaches (e.g. to use a pointer to pointer, or to use something else) is the stuff of engineering trade-offs, rather than absolute laws about what is "best" or "right" in some absolute sense.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  6. #6
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    in my example, I'd actually rather pass a non-const reference to the pointer, but then again, that's really just personal preference. either way would be equally effective.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    If you pass a pointer to pointer you must consider the case where the pointer to pointer is null. It also causes ambiguity to the caller: is the parameters allowed to be null? References solves these two problems.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Quote Originally Posted by Elysia View Post
    If you pass a pointer to pointer you must consider the case where the pointer to pointer is null. It also causes ambiguity to the caller: is the parameters allowed to be null? References solves these two problems.
    of course, there are situations where a pointer to pointer should be null, and that would indicate some particular condition. that sort of thing is best handled by good documentation.

    the point here is really that you just need to use the right tool for the job. sometimes it's a regular pointer, sometimes it's a reference to a pointer, and other times it's a pointer to a pointer.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    int main ( int argc, char **argv )

    Your first exposure to pointers to pointers.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    Registered User Vespasian's Avatar
    Join Date
    Aug 2011
    Posts
    181
    One practical reason why pointer to pointers are conceived lies within its use in linked lists:

    This is demonstrated in Nick Parlantes free online linked list tutorial where he prompts the reader to write a simple push function that pushes a node in a linked list:


    Quote Originally Posted by Nick Parlante
    Unfortunately Push() written in C suffers from a basic problem: what should be the
    parameters to Push()? This is, unfortunately, a sticky area in C. There's a nice, obvious
    way to write Push() which looks right but is wrong. Seeing exactly how it doesn't work
    will provide an excuse for more practice with memory drawings, motivate the correct
    solution, and just generally make you a better programmer....
    Code:
    void WrongPush(struct node* head, int data) {
       struct node* newNode = malloc(sizeof(struct node));
       newNode->data = data;
       newNode->next = head;
       head = newNode; // NO this line does not work!
    }
    
    void WrongPushTest() {
       List head = BuildTwoThree();
       WrongPush(head, 1); // try to push a 1 on front -- doesn't work
    }
    Quote Originally Posted by Nick Parlante
    WrongPush() is very close to being correct. It takes the correct 3-Step Link In and puts it an almost correct context. The problem is all in the very last line where the 3-Step Link
    In dictates that we change the head pointer to refer to the new node. What does the line head = newNode; do in WrongPush()? It sets a head pointer, but not the right one. It sets the variable named head local to WrongPush(). It does not in any way change the variable named head we really cared about which is back in the caller WrontPushTest().

    We are bumping into a basic "feature" of the C language that changes to local parameters
    are never reflected back in the caller's memory. This is a traditional tricky area of C
    programming. We will present the traditional "reference parameter" solution to this
    problem, but you may want to consult another C resource for further information.

    We need Push() to be able to change some of the caller's memory — namely the head variable. The traditional method to allow a function to change its caller's memory is to pass a pointer to the caller's memory instead of a copy. So in C, to change an int in the caller, pass a int* instead. To change a struct fraction, pass a struct fraction* intead.
    To change an X, pass an X*. So in this case, the value we want to change is struct node*, so we pass a struct node** instead. The two stars (**) are a little scary, but really it's just a straight application of the rule. It just happens that the value we want to change already has one star (*), so the parameter to change it has two (**).
    Or put another way: the type of the head pointer is "pointer to a struct node." In order to change that pointer, we need to pass a pointer to it, which will be a "pointer to a pointer to a struct node".

    Instead of defining WrongPush(struct node* head, int data); we define Push(struct node** headRef, int data);. The first form passes a copy of the head pointer. The second, correct form passes a pointer to the head pointer. The rule is: to modify caller memory, pass a pointer to that memory. The parameter has the word "ref" in it as a reminder that this is a "reference" (struct node**) pointer to the head pointer instead of an ordinary (struct node*) copy of the head pointer.

    Quote Originally Posted by Nick Parlante
    Here are Push() and PushTest() written correctly. The list is passed via a pointer to the head pointer. In the code, this amounts to use of '&' on the parameter in the caller and use of '*' on the parameter in the callee. Inside Push(), the pointer to the head pointer is named "headRef" instead of just "head" as a reminder that it is not just a simple head pointer..
    Code:
    /*
    Takes a list and a data value.
    Creates a new link with the given data and pushes
    it onto the front of the list.
    The list is not passed in by its head pointer.
    Instead the list is passed in as a "reference" pointer
    to the head pointer -- this allows us
    to modify the caller's memory.
    */
    void Push(struct node** headRef, int data) {
       struct node* newNode = malloc(sizeof(struct node));
       newNode->data = data;
       newNode->next = *headRef; // The '*' to dereferences back to the real head
       *headRef = newNode; // ditto
    }
    
    void PushTest() {
       struct node* head = BuildTwoThree();// suppose this returns the list {2, 3}
       Push(&head, 1); // note the &
       Push(&head, 13);
    // head is now the list {13, 1, 2, 3}
    }

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    An auxiliary discussion in this thread was moved to argv in the global main function and array or pointers to pointers.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Vespasian View Post
    One practical reason why pointer to pointers are conceived lies within its use in linked lists:

    This is demonstrated in Nick Parlantes free online linked list tutorial where he prompts the reader to write a simple push function that pushes a node in a linked list:
    In C, I might have agreed with you, but not with C++. This isn't good C++ code, and therefore is not a good example of the advantages of pointers to pointers for C++, which ultimately is the language we're talking about.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by c_weed View Post
    I'm having trouble understanding why pointers to pointers arise and am hoping someone could give me a very high level general explanation. For example
    int **myVar

    One thing I don't understand is int *myVar could be an array of int's - ok. But I don't see how if follows that int **myVar is an array of array's of int's because myVar still just points to a single address in memory. No matter how many * it always is just one address.
    That's not really correct since arrays of arrays do not decay like that. Only the first dimension would become a pointer and the other dimension would stay an array type, so you actually end up with Foo(*)[N]. And this is true for arrays with n-dimensions. What happens when you use pointers to simulate arrays of arrays like your talking about, is a different story. You will allocate a bunch of pointers that point to the other regions of contiguous memory.

    So in the array-like structure, you have a flat array that is very clearly just addresses:
    Code:
     [ 0x01 | 0x02 | 0x03 | ... 0x0n ]
    Name a type that would be capable of pointing to the first element: Foo** -- because the first element is itself a pointer type. You are, in effect, pointing to a location that points to a location containing an object of interest.

  14. #14
    Registered User Vespasian's Avatar
    Join Date
    Aug 2011
    Posts
    181
    Quote Originally Posted by Elysia View Post
    In C, I might have agreed with you, but not with C++. This isn't good C++ code, and therefore is not a good example of the advantages of pointers to pointers for C++, which ultimately is the language we're talking about.
    I heard this before but never understood why so. Is there a better/shorter way in C++? Through objects?

  15. #15
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Ideally, you would indeed abstract this into some class, like std::list does. When you do that, pointers to pointers just never manifest themselves. Also, we would prefer to use a reference to a pointer instead of a pointer to pointer.
    Also note that malloc in C++ is dangerous because it does not call constructors. Therefore, it is recommended to use new/delete instead. Of course, avoiding using them altogether is usually the best option.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. size of struct with pointers and function pointers
    By sdsjohnny in forum C Programming
    Replies: 3
    Last Post: 07-02-2010, 05:19 AM
  2. Storing function pointers in generic pointers
    By Boxknife in forum C Programming
    Replies: 6
    Last Post: 08-01-2009, 01:33 PM
  3. Pointers to objects -- passing and returning pointers
    By 1veedo in forum C++ Programming
    Replies: 4
    Last Post: 04-04-2008, 11:42 AM
  4. weak pointers and use_count smart pointers
    By Mario F. in forum C++ Programming
    Replies: 2
    Last Post: 07-29-2006, 07:54 AM
  5. pointers to constants and constant pointers
    By homeyg in forum C++ Programming
    Replies: 1
    Last Post: 06-18-2005, 12:02 AM