In int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
, 1, 2, and 3 initialize an array of 3 int
. An array is a contiguously allocated set of objects, so 1, 2, and 3 are contiguous in memory. 4, 5, and 6 initialize another array of 3 int
, and so do 7, 8, and 9. These three arrays of 3 int
are themselves another array, an array of 3 arrays of 3 int
. Since an array is a contiguously allocated set of objects, the 3 arrays are contiguous in memory. So the 4, 5, and 6 follow the 1, 2, and 3, and the 7, 8, and 9 followed the 4, 5, and 6.
So the overall effect is that 1, 2, 3, 4, 5, 6, 7, 8, and 9 are contiguous and consecutive in memory.
*((arr+i*n) + j)
uses this fact to calculate the location of the element in row i
and column j
. The starting address of the array is arr
. i*n
is the number of elements from the start to row i
. That is, each row has n
elements, so i
rows have i*n
elements. Then j
is the number of elements from the start of the row to the element in column j
of that row. So arr + i*n + j
is where the element in row i
, column j
is, and *(arr + i*n + j)
is that element. The extra parentheses in *((arr+i*n) + j)
are unnecessary.
This code abuses the C type model. In main
, arr
is an array of 3 arrays of 3 int
. When main
calls print
, it passes (int *)arr
. This passes a pointer to an int
instead of a pointer to an array of 3 int
, and then print
bypasses the normal application of array types to accessing the memory. Technically, the behavior of this code is not defined by the C standard, but it works in many C implementations.