How does `* ((* arr + (i * 3)) + j)` work when printing a two-dimensional array `arr`?
code:
#include <stdio.h>
int main(void)
{
int arr[2][3] = {{1,2,3},{4,5,6}};
int i, j;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
{
printf("%d ", *((*arr+(i*3))+j));
}
printf("\n");
}
return 0;
}
I'm surprised how the above code gives the result:
1 2 3
4 5 6
I know what *(arr+i) == arr[i]
, as well as what I should be using arr[i][j]
instead of making things more complicated, but I don't understand how it works *((*arr+(i*3))+j)
.
In my understanding *((*arr+(i*3))+j)
it can be simplified to *((*arr)+(i*3)+j)
as *
(indirection operator) has the biggest advantage here.
So when it i
is zero and j
iterates through, from 0 to 2, the expression is the same as arr[0][j]
that which prints the integers of the first subarray.
My confusion grows when i
it becomes 1. The next three expressions are *((*arr)+(1*3)+0)
, *((*arr)+(1*3)+1)
and *((*arr)+(1*3)+2)
, which can be simplified to arr[0][3]
, arr[0][4]
and arr[0][5]
.
How does it print the last three values?
int arr[2][3] = {{1,2,3},{4,5,6}};
In mind:
1 | 2 | 3 | 4 | 5 | 6
each number on an adjacent memory "cell" the size of an int, in this order
Second line: i = 1 j = 0
*((*arr+(i*3))+j)) means *((*arr + 3) + 0)-> *({1, 2, 3} + 3) -> *({4, 5, 6}) = 4
Keep in mind that is x[n]
equivalent *(x + n)
regardless of x
or n
. (This also means, which is arr[1]
equivalent *(arr + 1)
, *(1 + arr)
and therefore 1[arr]
, which I find amusing)
Here arr[1][0]
: x
is arr[1]
and n
is 0
, therefore the first equivalence: The *(arr[1] + 0)
second x
is equal arr
and n
equal 1
, therefore *(*(arr + 1) + 0)
.
Finally arr + 1
means address in arr + sizeof(*arr)
, which means: is
(arr + 1)
equivalent (*arr + 3)
because there *arr
is arr[0]
, which is of typeint[3]
The C language stores multidimensional arrays in so-called row order, so when you declare int arr[2][3]
, memory is actually laid out with the array elements occurring next to each other in memory in this sequence:
arr[0][0] arr[0][1] arr[0][2] arr[1][0] arr[1][1] arr[1][2]
This has several implications that are useful to know:
-
arr[1]
acts like a pointer to a one-dimensional array. how it happens when you declareint **arr;
. - The initializers in your initialization list are copied to memory locations in exactly the order in which you list them.
The array is stored in your memory, for example:
_____ _____ _____ _____ _____ _____
| 1 | | 2 | | 3 | | 4 | | 5 | | 6 |
----- ----- ----- ----- ----- -----
So, even though it is stored in a two-dimensional array, it infects memory allocation at compile time. When you write [1] [1], the compiler refers to the position (1 * (no of columns) + 1) in front, that is, 4 positions from the beginning.
See http://www.fredosaurus.com/notes-cpp/arrayptr/23two-dim-array-memory-layout.html