Perhaps the best way to understand is to compare examples of declarations with and without brackets. Just like in an expression brackets are used to affect the precedence of various parts of the declaration.
For instance, starting simple,
int* array[10];
is an array of ten pointers. But what if you want a pointer to an array instead of an array of pointers? That would be
int (*ptr)[10];
The brackets mean that ptr
is firstly a pointer, and it's only when that pointer is dereferenced that you get to the array.
So this example
int* (array[10]);
is the same as the first example. The superfluous brackets make clear that array
is firstly an array and that only when you get to the elements of the array that you have a pointer. What the above examples are saying is that in a declaration (just like in an expression) []
has higher precedence than *
and if you want it the other way round you have to use brackets.
So going to the original example
static const byte (*colorMapping[LAYER_SIZE]) [ROWS][COLS];
Looking inside the brackets first we have an array of pointers (just like my first example above). Then looking outside the brackets we see that each pointer points to a two dimensional array.
If we missed out the brackets
static const byte *colorMapping[LAYER_SIZE][ROWS][COLS];
then we'd have a three dimensional array of pointers.
Hopefully that's clear.