Casting is OK. What really can be questionable is to use a pointer to an element inside one subarray to access elements in another: although the operation is clearly defined at a lower level (everything is correctly aligned, not populated, the types are the same, ...), my view about standard C was that it was formulated in such a way as to allow border validation implementations to be standards.
Note, however, that linear movement of a multidimensional array may nevertheless be acceptable, since a pointer pointing after a subarray (which usually should not be dereferenced) also turns out to be a pointer to the first element of the next subarray. This thought leads to the fact that pointer arithmetic is nonassociative:
, , (int *)arr + 13, undefined 1 , ((int *)arr + 10) + 3.
, , , .. (int *)((char *)arr + 13 * sizeof (int)), , , , .
, , - , .
1 C11, 6.5.6 Β§8
[...] , ; undefined. [...]