is there even a difference between 'bool[][1]' and 'bool**' [parameters]
? |
Yes
A
bool[][1]
parameter accepts (the address of the first element of) an N by 1
array, where N is a positive integer, e.g.
1 2 3 4
|
bool arr1[][1] = {true};
bool arr2[][1] = {true, false};
bool arr5[][1] = {true, true, false, true, false};
// etc
|
As the parameter is an array, the function (or rather, the compiler) assumes that the elements are
laid out contiguously in memory, as they are for arrays of all sizes, 2D and otherwise.
The compiler also know that that
all the elements are in the same size, so when you give it the row and column, it can work out where an element is in memory with respect to the start address: row x <row length + col. (This need to know the row length -- or nuber of columns -- is why you can it the second size in e.g. arr[][3].)
A
bool**
parameter accepts any double pointer to bool -- pointers to pointers of single bools and bool* arrays, but not 2D bool arrays.
Here, ppfoo is a bool**
1 2 3
|
bool foo = true;
bool* pfoo = &foo;
bool** ppfoo = &pfoo;
|
And so is pbar in this snippet
1 2 3 4 5 6 7
|
bool a_bool = true;
bool another_bool = false;
bool bool_array = {true, false, true};
bool* bar[] = {&a_bool, &another_bool, bool_array};
bool** pbar = bar;
|
Now, if you create a bool* array which contains bool arrays of the same size, then the bool** array can
pretend to be an array. But (a) the memory layout is not contiguous, and (b) not all the involved array elements are the same size. So it is not actually an array.
(The char** array contains bool* pointers which will typically be a 32 or 64 bit values, compared with the 8 bits generally used to store bool values. So moving from row to row requires the compiler to move a pointer by 32 or 64 bits, where it's only 8 bits from column to column (or element to element, on the same row.)
The conclusion?
Given the issues to do with memory layout, you can hopefully see why the C++ compiler does not allow you to cast from array address to a pointer to pointer. So that makes (real) 2D arrays awkward to use with functions (but not impossible, if you use templates).
But is doesn't stop you from char** pretend arrays, These pretend arrays are particularly popular with people who haven't yet seen the light and realised how evil they really are! After all, they can even be allocated dynamically on the heap.
(I do note here, for those who know the C++ standard library, that using a std::vector of std::vectors isn't quite as evil, but it's not the best solution, either.)
For further information, see these cplusplus.com articles:
Multidimentional arrays are evil
http://www.cplusplus.com/articles/G8hv0pDG/
- for why you shouldn't use multi-dimensional arrays outside of a programming exercise.
Multi-Dimensional Arrays
http://www.cplusplus.com/articles/NAUq5Di1/
- for examples of how to use "evil" multi-dimensional arrays
The difference between pointers and arrays
http://www.cplusplus.com/articles/NUCRko23/
- and
Array is not pointer
http://www.cplusplus.com/articles/2TA0RXSz/
- for more about the differences between pointers and arrays
So, given that multi-dimensional arrays are evil, what should you use instead? As the "evil" article mentioned above says, you should use a 1D array and handle the translation from 2D to 1D indexing youself (rather than letting the compiler do it for you.) Of course, as you're a C++ programmer (I hope!) you'd abstract this translation mechanism nicely away inside a class. As does the sample that ends the article.
Andy