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?
source to share
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]
source to share
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.
source to share
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
source to share