Suppose you had a two-dimensional array:
uint32_t array[][2] = {
{1, 2},
{3, 4},
{5, 6},
{7, 8},
{9, 10},
{0, 0}
};
We know that this isn't really a "two-dimensional" array. It's really an array of arrays. array is an array of somethings, and what each something is is a little array of two uint32_t's. So array[0] is an array of two uint32_t's, array[1] is an array of two uint32_t's, etc.
Suppose that, for some reason, you wanted to manipulate pointers to rows in this array. Perhaps you want to move down the array using code like this:
sometype p;
p = array;
while(p is not at the end of array) {
do something with the array pointed to by p;
p++;
}
Or suppose you wanted to keep track of certain rows in the array, again via pointers:
sometype row2, row4;
row2 = &array[2];
row4 = &array[4];
We know that this makes sense, because array is an array of something, so array[2] is the second (really the third, because 0-based) something, ao &array[2] is a pointer to that something. And, again, in this case the "something" is an array of two uint32_t's.
But the question is, what is sometype? We want p, row2, and row4 to have type "pointer to array of two uint32_t". This is an unusual type, but it does exist, and it's the type you've been asking about:
uint32_t (*p)[2];
This says that p is precisely a pointer to an array of two uint32_t's. The big and obvious subquestion, of course, is: what the heck are those parentheses doing around the (*p) part? And the answer is that without them, we'd have
uint32_t *p[2]; /* not what we want */
and this would declare p as an array of two pointers to uint32_t. In C, there are precedence relationships in declarations just like there are in expressions, and [] binds more tightly than *. So if you want to say that p is a pointer first (and that what it's a pointer to is any array), you need the parentheses:
uint32_t (*p)[2];
So now we can rewrite that loop I wrote earlier that tries to scan the array:
uint32_t (*p)[2];
p = array;
while((*p)[0] != 0 || (*p)[1] != 0) {
printf("next row: %d, %d\n", (*p)[0], (*p)[1]);
p++;
}
This works, and I encourage you to compile and run with it. There are some more "funny" parentheses, again because of precedence. Our p is indeed a pointer to a little array of two uint32's. So (*p) is an array of two uint32's. So (*p)[0] is the first element of the pointed-to array, and (*p)[1] is the second. But without the parentheses we'd have *p[0] and *p[1], and those would try to, basically, treat p as an array first and a pointer second, which is not what we want. (Actually, as @sj95126 and I are discussing in the comments, a notation like *p[1] does work — at first I was thinking it would be an error — but it does something different, namely to access the [0] element of the subarray one past p, not the [1] element of the array at p.)
Finally, it may be useful at this point to observe that for "complicated" types such as pointers to arrays, C's typedef mechanism can sometimes make things clearer. Earlier I wrote, as pseudocode,
sometype p;
because we weren't quite sure what type to use. But, now that we know, and if we wanted more convenient declarations for variables like p, row2, and row4, we could write
typedef uint32_2 (*sometype)[2];
This is just like our earlier declaration of p, except that it has that extra keyword typedef out at the front. This changes things: we are not declaring "sometype" as a pointer to an array of two uint32_t's; we are instead declaring "sometype" as an alias or shorthand for the type, "pointer to an array of two uint32_t's". We can then literally say things like
sometype p;
or
sometype row2, row4;
The typedef only helps when declaring p, row2, and row4, though. Down in the expressions where we use them, we'll still need the extra parentheses. And typedefs incorporating pointers can also be confusing (more confusing than they're convenient, that is), so many programmers recommend avoiding pointer typedefs. (Also of course "sometype" is a horrible name, in practice. If you really wrote this you'd want something like typedef uint32_2 (*ptrary2)[2]; ptrary2 p;.)
For a bit more discussion on pointers to arrays, see question 6.13 in the old C FAQ list.