Kevin Richards
Kevin Richards

Reputation: 570

Behavior of 2D arrays

I have created a 2D array, and tried to print certain values as shown below:

int a[2][2] = { {1, 2}, 
                {3, 4}};

printf("%d %d\n", *(a+1)[0], ((int *)a+1)[0]);

The output is:

3 2

I understand why 3 is the first output (a+1 points to the second row, and we print its 0th element.

My question is regarding the second output, i.e., 2. My guess is that due to typecasting a as int *, the 2D array is treated like a 1D array, and thus a+1 acts as pointer to the 2nd element, and so we get the output as 2.

Are my assumptions correct or is there some other logic behind this?
Also, originally what is the type of a when treated as pointer int (*)[2] or int **?

Upvotes: 16

Views: 645

Answers (4)

haccks
haccks

Reputation: 106012

Are my assumptions correct or is there some other logic behind this?

Yes.

*(a+1)[0] is equivalent to a[1][0].
((int *)a+1)[0] is equivalent to a[0][1].

Explanation:

a decays to pointer to first element of 2D array, i.e to the first row. *a dereferences that row which is an array of 2 int. Therefore *a can be treated as an array name of first row which further decay to pointer to its first element, i.e 1. *a + 1 will give the pointer to second element. Dereferencing *a + 1 will give 1. So:

((int *)a+1)[0] == *( ((int *)a+1 )+ 0) 
                == *( ((int *)a + 0) + 1) 
                == a[0][1]   

Note that a, *a, &a, &a[0] and &a[0][0] all have the same address value although they are of different types. After decay, a is of type int (*)[2]. Casting it to int * just makes the address value to type int * and the arithmetic (int *)a+1 gives the address of second element.

Also, originally what is the type of a when treated as pointer (int (*)[2] or int **?

It becomes of type pointer to array of 2 int, i.e int (*)[2]

Upvotes: 9

Utkan Gezer
Utkan Gezer

Reputation: 3069

The key thing to recognize here is that the a there holds the value of the address where the first row is located at. Since the whole array starts from the same location as that, the whole array also has the same address value; same for the very first element.

In C terms:

&a == &a[0];
&a == &a[0][0];
&a[0] == &a[0][0];
// all of these hold true, evaluate into 1
// cast them if you want, looks ugly, but whatever...
&a == (int (*)[2][2]) &a[0];
&a == (int (*)[2][2]) &a[0][0];
&a[0] == (int (*)[2]) &a[0][0];

For this reason, when you cast the a to int *, it simply becomes 1-to-1 equivalent to &a[0][0] both by the means of type and the value. If you were to apply those operations to &a[0][0]:

(&a[0][0] + 1)[0];
(a[0] + 1)[0];
*(a[0] + 1);
a[0][1];

As for the type of a when treated as a pointer, although I am not certain, should be int (*)[2].

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 310970

When you wrote expression

(int *)a

then logically the original array can be considered as a one-dimensional array the following way

int a[4] = { 1, 2, 3, 4 };

So expression a points to the first element equal to 1 of this imaginary array. Expression ( a + 1 ) points to the second element of the imaginary array equal to 2 and expression ( a + 1 )[0] returns reference to this element that is you get 2.

Upvotes: 10

nullptr
nullptr

Reputation: 11058

A 2D-array is essentially a single-dimensional array with some additional compiler's knowledge.

When you cast a to int*, you remove this knowledge, and it's treated like a normal single-dimensional array (which in your case looks in memory like 1 2 3 4).

Upvotes: 3

Related Questions