There is a rough equivalency between a pointer to an object and an array of objects. So if you have something like:

int *p;

you can access the CONTENTS of p with:

int q = *p; // q gets the CONTENTS of what p points to

but also with:

int q = p[0];

In this case, p[0] is equivalent to *p. So just extend this to greater indirection:

int **p;

int q = **p;

int q = p[0][0];

In your example declaration, you are mixing the * and [] syntax which is OK...

So you have the equivalent of:

int (* p[2])[2];

which is declaring an array of 2 int pointers 2 times. (The parentheses are redundant here.)

So an item in this (confusing mess) can be accessed like:

q = p[0][0][0];

or

q = *p[0][0];

int *p;

you can access the CONTENTS of p with:

int q = *p; // q gets the CONTENTS of what p points to

but also with:

int q = p[0];

In this case, p[0] is equivalent to *p. So just extend this to greater indirection:

int **p;

int q = **p;

int q = p[0][0];

In your example declaration, you are mixing the * and [] syntax which is OK...

So you have the equivalent of:

int (* p[2])[2];

which is declaring an array of 2 int pointers 2 times. (The parentheses are redundant here.)

So an item in this (confusing mess) can be accessed like:

q = p[0][0][0];

or

q = *p[0][0];