Search

6.14 — Pointers to pointers and dynamic multidimensional arrays

This lesson is optional, for advanced readers who want to learn more about C++. No future lessons build on this lesson.

A pointer to a pointer is exactly what you’d expect: a pointer that holds the address of another pointer.

Pointers to pointers

A normal pointer to an int is declared using a single asterisk:

A pointer to a pointer to an int is declared using two asterisks

A pointer to a pointer works just like a normal pointer — you can dereference it to retrieve the value pointed to. And because that value is itself a pointer, you can dereference it again to get to the underlying value. These dereferences can be done consecutively:

The above program prints:

5
5

Note that you can not set a pointer to a pointer directly to a value:

This is because the address of operator (operator&) requires an lvalue, but &value is an rvalue.

However, a pointer to a pointer can be set to null:

Arrays of pointers

Pointers to pointers have a few uses. The most common use is to dynamically allocate an array of pointers:

This works just like a standard dynamically allocated array, except the array elements are of type “pointer to integer” instead of integer.

Two-dimensional dynamically allocated arrays

Another common use for pointers to pointers is to facilitate dynamically allocated multidimensional arrays (see 6.5 -- Multidimensional Arrays for a review of multidimensional arrays).

Unlike a two dimensional fixed array, which can easily be declared like this:

Dynamically allocating a two-dimensional array is a little more challenging. You may be tempted to try something like this:

But it won’t work.

There are two possible solutions here. If the rightmost array dimension is a compile-time constant, you can do this:

The parenthesis are required here to ensure proper precedence. In C++11 or newer, this is a good place to use automatic type deduction:

Unfortunately, this relatively simple solution doesn’t work if any non-leftmost array dimension isn’t a compile-time constant. In that case, we have to get a little more complicated. First, we allocate an array of pointers (as per above). Then we iterate through the array of pointers and allocate a dynamic array for each array element. Our dynamic two-dimensional array is a dynamic one-dimensional array of dynamic one-dimensional arrays!

We can then access our array like usual:

With this method, because each array column is dynamically allocated independently, it’s possible to make dynamically allocated two dimensional arrays that are not rectangular. For example, we can make a triangle-shaped array:

In the above example, note that array[0] is an array of length 1, array[1] is an array of length 2, etc…

Deallocating a dynamically allocated two-dimensional array using this method requires a loop as well:

Note that we delete the array in the opposite order that we created it (elements first, then the array itself). If we delete array before the array elements, then we’d have to access deallocated memory to delete the array elements. And that would result in undefined behavior.

Because allocating and deallocating two-dimensional arrays is complex and easy to mess up, it’s often easier to “flatten” a two-dimensional array (of size x by y) into a one-dimensional array of size x * y:

Simple math can then be used to convert a row and column index for a rectangular two-dimensional array into a single index for a one-dimensional array:

Passing a pointer by address

Much like we can use a pointer parameter to change the actual value of the underlying argument passed in, we can pass a pointer to a pointer to a function and use that pointer to change the value of the pointer it points to (confused yet?).

However, if we want a function to be able to modify what a pointer argument points to, this is generally better done using a reference to a pointer instead. So we won’t talk about it further here.

We’ll talk more about pass by address and pass by reference in the next chapter.

Pointer to a pointers to a pointer to…

It’s also possible to declare a pointer to a pointer to a pointer:

These can be used to dynamically allocate a three-dimensional array. However, doing so would require a loop inside a loop, and are extremely complicated to get correct.

You can even declare a pointer to a pointer to a pointer to a pointer:

Or higher, if you wish.

However, in reality these don’t see much use because it’s not often you need so much indirection.

Conclusion

We recommend avoiding using pointers to pointers unless no other options are available, because they’re complicated to use and potentially dangerous. It’s easy enough to dereference a null or dangling pointer with normal pointers — it’s doubly easy with a pointer to a pointer since you have to do a double-dereference to get to the underlying value!


6.15 -- An introduction to std::array
Index
6.13 -- Void pointers

120 comments to 6.14 — Pointers to pointers and dynamic multidimensional arrays

  • George

    Thank you. Very crisp and concise refresher tutorial. Thank you!

  • Ambareesh

    " right-most array dimension is a compile-time constant "

    Why is "right-most" relevant? For e.g if the dimensions needed were arr[10][5], why is it easier if 5 is known before hand? Is there any limitation at all on the left one, at all?

    • Alex

      I changed "rightmost" to "non-leftmost", which is more accurate for arrays beyond two dimensions. The leftmost dimension can be deduced if there is an initializer.

  • Nima

    Hello!
    Thank you for your awesome tutorials.
    I encounter with a piece of code which is weird for me!
    Can you explain why?

    So why cout prints differently in char array instead of printing address of second row of first column?
    Thank you so much!

    • behop

      It's covered in 6.8b - C-style string symbolic constants.

      >The answer is that std::cout makes some assumptions about your intent. If you pass it a non-char pointer, it will simply print the contents of that pointer (the address that the pointer is holding). However, if you pass it an object of type char* or const char*, it will assume you’re intending to print a string. Consequently, instead of printing the pointer’s value, it will print the string being pointed to instead!

  • Al

    There's something I don't get.

    declares a int*[5] pointer, i.e. a pointer to a 5 int array. Nevertheless

    initializes a dynamically allocated 2-D array. Now isn't this like magic?

    typeid(ptr).name() returns PA5_i, the same that is returned by new int[numArrays][5], but you actually gain access to a whole matrix. How?
    Why isn't ptr of type PA<numArray>_A5_i? Is there something going on behing the scenes?

    • nascardriver

      The value you use to initialize a type can't change the type. You said the type is `int(*)[5]`, nothing can change that.
      Remember that arrays decay to pointers, multidimensional arrays are no exception. Although the element type gets preserved, the length of the outer array is lost.

  • Chuggablimpdaddy

    I'm not getting something with this snippet.

    So, this array would look something like

    1    1    1    1    1
    1    1    1    1    1
    1    1    1    1    1
    1    1    1    1    1
    1    1    1    1    1
    1    1    1    1    1
    1    1    1    1    1
    1    1    1    1    1
    1    1    1    1    1

    which has 45 elements. And row 9 column four would be element 44. But, the equation in the code gives us 49.

    • Delwys Glokpor

      Don't forget this is cpp. The array indexes start at 0. Thus [9, 4] is actually the cell at the 10th row 5th column and its index is should be 49 as the indexes would go from 0 to 49.
      }

      • Chuggablimpdaddy

        Hey, thanks for the response Delwys.

        So it actually looks like...

        array[9][4]
        item is the 0 in the 10th row, 5th column
        which is the 50th item and is expressed as a member of a 1-dimensional array as array[49]
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    1
        1    1    1    1    0

  • Rohit Alawadhi

    Thank you so much for such quality, premium content. Highly appreciate the efforts. I am almost done with the tutorials. Could you suggest me [your website, if you have one] with same level of premium material on data structure , multi threading and algorithms? If not, then something that is as fluid as your writing style and at the same time is comprehensive enough as well.
    :)

  • Sid22

    I am trying to implement a simple program that has objects of "Student" class dynamically allocated and then sorted in the array in alphabetical order by their names (as in a dictionary). For example: the object with the string "Ana" as the name will be at an earlier index of the array of objects , whereas the object with "Azra" as the name will come after.

    -> The problem:

    The logic seems fine and the code compiles on Visul Studio but crashes at runtime midway through the program after taking necessary info for initialization of object members (which has been implemented through

    in the default constructor.

    MY code:

    • nascardriver

      Line 51: What's the maximum value of `j`?
      Line 54: Which index are you accessing when `j` is at its maximum?

      - Initialize variables with brace initialization for higher type safety.
      - Use ++prefix. postfix++ is slower.
      - `std::string` can be compared with `<`. (`ptr[j].get_name() < ptr[j + 1].get_name()`).

      • Sid22

        SOLVED IT!
        Made the following changes:

        • Kurt Van Bever

          Better do it like this I think.

  • Pankaj

    How to de-allocate for allocation,

    int (*ptr)[5] = new int [10][5];

    auto ptr = new int[10][5];

  • hellmet

    I don't get this...

    Could you explain this please?

    • The 5 is the size of the inner array. The size of the outer array is lost when the array decays, just like with one-dimensional arrays.
      The syntax is hard to learn, like the syntax of function pointers and array references. Fortunately you don't need them unless you want to have full control over the memory. You'll learn about easier and safer alternatives later.

      • hellmet

        Hmmm how seemingly counterintuitive :P

        Okay so you mean new int[10] decays into a pointer which means I'm then doing new (*int)[5]? Hence the syntax? But the array is still 10 rows and 5 cols right? I tried to verify, but doesn't seem to crash the code hence unsure of the bounds set up.

        • > Okay so you mean new int[10] decays into a pointer which means I'm then doing new (*int)[5]?
          I honestly don't know when the array decays. I never needed it and I don't think I ever will, so I won't look it up.

          > the array is still 10 rows and 5 cols right?
          Rows and columns are a nice aide, but they can't be used to communicate, because a 2D array can be imagined multiple ways, such that rows and columns could be swapped.
          "Outer array" and "inner array" work better. When you grab an element from the outer array, you get an inner array.
          The outer array has 10 elements. The inner arrays have 5 elements each.
          Trying to verify this by trying to crash or by trying to get weird values is bad. It's undefined behavior, you can't draw any conclusions from the test.
          Compile-time errors are more reliable

          • hellmet

            Oh my god that makes so much sense! The 'inner' and 'outer' array explanation on point! Amazing example with the compiler-error! Thank you very much!

  • Justin

    For skilled programmers (even though I'm only learning c++),

    I need help with a C2062 error that I don't understand. I started trying to code natural merge sort. Look at this code.

    This is the error that I get:
    (17,59): error C2062:  type 'int' unexpected
    I think that it's saying that there's something wrong with the declaration of runLength, but I don't know what's wrong in my code.

    Also, how do I shorten the long for loop? It's 89 characters wide!

  • Atas

    Hello! Is there any way to rewrite this line of code:

    such that the '*' is next to the type, not the variable name? I much prefer my *-s and &-s next to the type name.

    The following

    doesn't compile and I can not quite grasp why and what exactly is happening. The syntax for declaring arrays is confusing. It says "cannot covert 'int (*)[5]' to 'int *[5]'". I presume int (*)[5] is an array of 5 pointers to int, but then what is int *[5]?

    • > int (*)[5] is an array of 5 pointers to int
      int (*)[5] is a pointer to an array of 5 ints.
      int *[5] is an array of 5 pointers to ints.

      • Atas

        > int (*)[5] is a pointer to an array of 5 ints.
        But doesn't this make the lhs of the statement

        a pointer to an array of 5 ints? Could you please spell out the precendence on the rhs of the assignment as well, that's the most confusing part I think. I read it as follows: the second set of  square brackets [5] is a declaration of static array of 5 elements, where an element is the rest of the expression, i.e. new int[10]. Putting it all together I interpret the rhs as

        Is that anywhere near what's actually going on? :-)

        • It's the other way around.

          The length of `array` is 10, the length of `array[i]` is 5.

          Those are all the same

  • cdecde57

    I am having a problem printing a 2d array that is dynamically allocated like in this lesson.

    Please help.

    • Line 10: No, you're accessing invalid indexes, resulting in undefined behavior. If you want to 0-initialize your arrays, add empty curly braces to the end of line 8.

      If you have problems with something, always state the expected- and observed behavior. If you receive compiler warnings or errors, share those too.

      • cdecde57

        Alright. Thanks! It works now. I also understand why it now so thanks!

        I really read through this lesson and I think I understand how this is actually working now.

        We make an array. Then put that array into a loop to give values to each of the arrays in the array like normal, but instead giving it a value of like 3 we say that it is another array so it's like an array within an array.

        Correct me if I am wrong. I am just trying to figure out how essentially it works so I can do well in it.

        Thanks!

Leave a Comment

Put all code inside code tags: [code]your code here[/code]