If we change the sizes to make it easier to fit:
#define M 2
#define N 3
Then somewhat graphically the variable arr is something like this:
+-----+ +--------+--------+--------+--------+--------+--------+
| arr | ---> | [0][0] | [0][1] | [0][2] | [1][0] | [1][1] | [1][2] |
+-----+ +--------+--------+--------+--------+--------+--------+
arr is a pointer. It points to an array of arrays, where each element is an int.
That means you can dereference arr to get what it points to, and then use array indexing on that to get an int value:
int value = (*arr)[0][1];
Now if we take localArr instead, it's more like
+----------------+----------------+----------------+----------------+----------------+----------------+
| localArr[0][0] | localArr[0][1] | localArr[0][2] | localArr[1][0] | localArr[1][1] | localArr[1][2] |
+----------------+----------------+----------------+----------------+----------------+----------------+
localArr is an array of arrays, where each element is a int *.
So you need an int * to get a single element:
int *pointer = localArr[0][1];
arrand declared type oflocalArr. See any difference?arrandlocalArrhave different types.arris a pointer to an array of arrays ofint, whilelocalArris an array or arrays of pointers toint.