1

I am newbie in C++ and trying to understand one important thing. I have heard that all arrays are only pointer aliases and every action done by [] operator is converted to pointer dereference. See the example below (dynamic array).

auto **tab = new int*[2];
tab[0] = new int[2];
tab[1] = new int[2];

tab[0][0] = 1;
tab[0][1] = 2;
tab[1][0] = 3;
tab[1][1] = 4;

cout << "tab[0] address: " << &tab[0] << " with value: " << tab[0] << endl;
cout << "tab[1] address: " << &tab[1] << " with value: " << tab[1] << endl;
cout << "tab[0][0] address: " << &tab[0][0] << " with value: " << tab[0][0] << endl;
cout << "tab[0][1] address: " << &tab[0][1] << " with value: " << tab[0][1] << endl;
cout << "tab[1][0] address: " << &tab[1][0] << " with value: " << tab[1][0] << endl;
cout << "tab[1][1] address: " << &tab[1][1] << " with value: " << tab[1][1] << endl;

delete[] tab[0];
delete[] tab[1];
delete[] tab;

This is pretty simple program, which shows how pointer to pointer is represented in memory. It returns the following result:

tab[0] address: 0x7114d0 with value: 0x7114e0
tab[1] address: 0x7114d4 with value: 0x7114f0
tab[0][0] address: 0x7114e0 with value: 1
tab[0][1] address: 0x7114e4 with value: 2
tab[1][0] address: 0x7114f0 with value: 3
tab[1][1] address: 0x7114f4 with value: 4

Note: I fully understand how it works. I have two basic adresses, which points to the other second addresses. As a result dereferencing tab[0] returns memory address to the second dimension. Pseudocode:

*(0x7114d0) = 0x7114e0 -> 1

But the second example shows how static arrays works.

int tab[2][2] = {{1,2}, {3,4}};

cout << "tab[0] address: " << &tab[0] << " with value: " << tab[0] << endl;
cout << "tab[1] address: " << &tab[1] << " with value: " << tab[1] << endl;
cout << "tab[0][0] address: " << &tab[0][0] << " with value: " << tab[0][0] << endl;
cout << "tab[0][1] address: " << &tab[0][1] << " with value: " << tab[0][1] << endl;
cout << "tab[1][0] address: " << &tab[1][0] << " with value: " << tab[1][0] << endl;
cout << "tab[1][1] address: " << &tab[1][1] << " with value: " << tab[1][1] << endl;

Same as in the previous example I get results:

tab[0] address: 0x28fea0 with value: 0x28fea0
tab[1] address: 0x28fea8 with value: 0x28fea8
tab[0][0] address: 0x28fea0 with value: 1
tab[0][1] address: 0x28fea4 with value: 2
tab[1][0] address: 0x28fea8 with value: 3
tab[1][1] address: 0x28feac with value: 4

And in this point I have a huge problem how to understand this, bacause dereferencing 'tab[0]' returns the address of 'tab[0]' which also hold value(1)... Pseudocode

*(0x28fea0) = 0x28fea0 -> 1

And now how tu understand it? It seems that in memory static and dynamic multidimensional arrays are handle in two different ways. If arrays are in fact pointers how static arrays dereferencing works by the fact that tab[0] handle own address (pseudocode below)...

abba
  • 11
  • 2
  • 1
    1) "_have heard that all arrays are only pointer aliases_" You heard wrong. Arrays are not pointers. 2) "_And now how tu understand it?_" Consider reading a [good C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), where the concept of multi-dimensional arrays should be explained in detail. – Algirdas Preidžius Nov 03 '17 at 10:01
  • If you think arrays are just pointers, compare sizeof(pointer) and sizeof(array). If array is just a pointer, how does he know how long the data is? (I do not have the answer yet) – Vulpo Nov 03 '17 at 10:05
  • In your first fragment, you have an array of pointers (to arrays). In the second, you have an array of arrays (of size 2). The type `int[2]` is different from the type `int[]` (which generally decays to a pointer). If it helps, multi-dimensional arrays are not part of the C++ language - they are an interpretation we make of arrays-of-arrays (or arrays of pointers). – Toby Speight Nov 03 '17 at 10:15
  • _"I am newbie in C++ and trying to understand one important thing."_ The really important thing you need to understand is that you shouldn't need to write `new` or `delete` yourself because the language provides a Standard Library with ample types of containers to avoid you having to think about all that. Instead, you can use the stdlib to write code that does useful things, and if you ever find a niche use case for raw `new`/`delete`, you can worry about them then, when there's a reason. – underscore_d Nov 03 '17 at 10:21
  • Possible duplicate of [How do I declare a 2d array in C++ using new?](https://stackoverflow.com/questions/936687/how-do-i-declare-a-2d-array-in-c-using-new) – underscore_d Nov 03 '17 at 10:24
  • @underscore_d: We do not need low level details in common C++ programming, but understanding what happens behind the scenes is still usefull. After all container implementations are written in C++ and do use low level `new` and `delete`, which in turn use by default the good old C `malloc` and `free`... – Serge Ballesta Nov 03 '17 at 10:54
  • @SergeBallesta Sure, I'm not saying it's not useful to know, but I question whether it's useful to know _as a newbie_: whether newbies spending their time wrapping their head around manual memory management is the best use of their time in the early stages of their learning, instead of just using the ample facilities the language already provides for this stuff and getting on with writing programs that teach more generally useful concepts. Most newbies are probably not going to end up programming their own container implementations, after all, or at least not until quite some time later. – underscore_d Nov 03 '17 at 10:57
  • 1
    @underscore_d: I agree with you, but as an old dinosaur coming from C, the difference between a pointer and an array, and between a 2D array and a array of pointer seem quite important to me. – Serge Ballesta Nov 03 '17 at 11:02
  • @SergeBallesta Oh, definitely, and thanks for making sure that my other point doesn't detract from that. :) – underscore_d Nov 03 '17 at 11:08

1 Answers1

1

2D arrays and arrays of pointers are different animals, even if their syntax is the same.

This is an array of pointers to arrays:

auto **tab = new int*[2];
tab[0] = new int[2];
tab[1] = new int[2];

tab is an array of pointers, each element of tab points to a row, but the rows are allocated independently of each others and are not (necessarily) contiguous. In theory they could even have different sizes, which is common for arrays of C-strings.

This is a 2D array:

int tab[2][2];

It is an array of arrays. Here the rows are required to have same size and are contiguously allocated: &tab[0][2] is required to be the same as &tab[1][0]

But is is completely independant of static or dynamic allocation. Here is a static array of pointers:

int row1[2] = { 0, 1};
int row2[2] = { 2, 3};
int *tab[] = { row1, row2 }; // row1 and row2 decay to pointers to their first element;

And here is a dynamically allocated 2D array:

typedef int iarr2[2];
iarr2* dyntab = new iarr2[2];
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252