Why is these two are same things I saw one answer to this similar question but couldn't really understand.
Why *(a+i)
and a+i
doing the same work.
int a[1][2] = {1,2};
printf("%p or %p\n",*(a+0),a+0);
Why is these two are same things I saw one answer to this similar question but couldn't really understand.
Why *(a+i)
and a+i
doing the same work.
int a[1][2] = {1,2};
printf("%p or %p\n",*(a+0),a+0);
Array subscript brackets "[]" are defined in terms of pointer operations. a[i] is defined as *(a+i), assuming that a is of type array and i is an integer type. The name of an array by itself is evaluated as a pointer to its first element; integer addition to a pointer adds the integer times the sizeof the element to the pointer. (a+i) and *(a+1) are not the same - (a+i) is the address of an element in an array and *(a+i) is that element.
Thus a[0] becomes *(a+0), or *a, the first element of the array. A[1] becomes *(a+1), or *(a + (sizeof (*a) * 1), or *(address of the second element of the array), or just the second element of the array, as one would expect.
Except when it is the operand of the sizeof
or unary &
operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T
" is converted ("decays") to an expression of type "pointer to T
", and the value of the expression is the address of the first element of the array.
So given a declaration like
int a[100];
any time a
appears in an expression where it isn't the operand of the sizeof
or unary &
operators, the compiler will treat it as a pointer expression equivalent to &a[0]
(type int *
).
Now, what happens if a
is the operand of unary &
?
The address of an array is the same as the address of the first element of the array, which should be clear from the diagram below:
+------+
a: | a[0] |
+------+
| a[1] |
+------+
...
+------+
| a[99]|
+------+
The type of the expression &a
is int (*)[100]
(pointer to 100-element array of int
), but the value of the expression is the same as a
and &a[0]
(address of the first element).
Summarizing that in a table:
int a[100];
Expression Type "Decays" to
---------- ---- -----------
a int [100] int *
*a int n/a
&a int (*)[100] n/a
a[i] int n/a
&a[i] int * n/a
Again, the address of the array is the same as the address of the first element of the array, so &a
, a
, and &a[0]
will all yield the same address value (modulo any type conversions).
Adding 1 to a pointer yields the address of the next object of the pointed to type. IOW, if p
points to a 4-byte int
object, the result of p + 1
will be the address of the next 4-byte int
. If p
points to a 100-element array of 4-byte int
s, then p + 1
yields the address of the next 100-element array of int
.
The subscript operation a[i]
is defined as *(a + i)
- given the address a
, find the address of the i
'th object following a
and dereference the result.
This means that the value of *a
is the same as a[0]
- *a == *(a + 0) == a[0]
.
How does this apply to a 2D array, as in your example?
Given the declaration
int a[1][2];
the expression a
"decays" from type "1-element array of 2-element array of int
" (int [1][2]
) to "pointer to 2-element array of int
" (int (*)[2]
). The value of the expression a
is the address of the first element, but the first element has an array type itself. Thus, that value "decays" to a pointer to the first element of the subarray.
Here's a handy table to summarize:
int a[1][2];
Expression Type "Decays" to
---------- ---- -----------
a int [1][2] int (*)[2]
*a int [2] int *
&a int (*)[1][2] n/a
a[0] int [2] int *
*a[0] int n/a
&a[0] int (*)[2] n/a
a[0][0] int n/a
Again, &a
, a
, a[0]
, &a[0]
, and &a[0][0]
will all yield the same value, since the address of a[0][0]
is the same as the address of a[0]
which is the same as the address of a
.
C's doesn't really treat multidimensional arrays differently from single-dimensional ones. Arrays in C are just array of arrays
char a[2][3][4][5];
is an array 2 of array 3 of array 4 of array 5 of char.
Dereferencing/subscripting works the same for any "Array A of T":
sizeof(T)
With dereferencing/subscripting in C, when you speak of one, you speak of the other, because A[Index]
or Index[A]
is defined to be the same as *(A+Index)
or *(Index+A)
.
6.5.2.1p2
A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).
Since in char a[2][3][4][5];
a
is an array 2 of ( array 3 of array 4 of array 5 of char), a[1]
would give you ((char*)&a) + 1 * sizeof(char[3][4][5])
and the result would have type char[3][4][5]
.
Now here's where arrays are special — arrays aren't first-class objects in C. You can't have an r-value of an array type. When you attempt to obtain one, e.g., by passing the array to a function or an operator, the array immediately decays to a pointer to its first element so the char[3][4][5]
type of a[1]
immediately changes to char(*)[4][5]
.
6.5.2.1p3
Successive subscript operators designate an element of a multidimensional array object. If E is an n-dimensional array (n >= 2) with dimensions i x j x . . . x k, then E (used as other than an lvalue) is converted to a pointer to an (n - 1)-dimensional array with dimensions j x . . . x k. If the unary * operator is applied to this pointer explicitly, or implicitly as a result of subscripting, the result is the referenced (n - 1)-dimensional array, which itself is converted into a pointer if used as other than an lvalue. It follows from this that arrays are stored in row-major order (last subscript varies fastest).
This continues recursively until you've chomped off all of the dimensions (from right to left) and are left with a real type that doesn't decay. Effectively the decay of the intermediary arrays means that intermediary derefs/subscripts don't really fetch anything — they're simply additions to a base address.
Some examples with char a[2][3][4][5];
:
#include <stdio.h>
char a[2][3][4][5];
#define ASSERT_TP(Expr,Tp) _Generic(Expr,Tp: (char*)(Expr))
int main()
{
printf("%zd\n", ASSERT_TP(a,char(*)[3][4][5])
- (char*)a); //0
printf("%zd\n", ASSERT_TP(a[1],char(*)[4][5])
- (char*)a); //60 == 1 * (3*4*5)
printf("%zd\n", ASSERT_TP(a[1][1],char(*)[5])
- (char*)a); //80 == 1 * (3*4*5) + 1 * (4*5)
}
Applied to your example:
int a[1][2] = {1,2}; // a decays to ptr to 1st element,
//i.e. to `int (*a)[2]`
printf("%p or %p\n",
*(a+0), // == a[0]; ((char*)&a) + 0*sizeof(int[2]);
// type is int[2], which decays to int*
a+0); // == a (after decay); (char*)&a + 0*sizeof(int[2]);
//type is still `int(*)[2]` (because no derefing)
Because the deref in *(a+0)
didn't hit the real type yet, there was no fetch, just an addition to a base pointer
with type adjustement. Since the addition added 0, the value didn't change it remained the same as that of a
decayed to a pointer to its first element (== a+0
) or even &a
(which would have the same numerical address but its type would be int (*)[1][2]
).
An array can decay to pointer to its first element. In your example plain a
will decay to &a[0]
. And for any array or pointer a
and index i
, the expression a[i]
is exactly equal to *(a + i)
.
Also, if you lay out your array how it would look like in memory, it would be
+---------+---------+ | a[0][0] | a[0][1] | +---------+---------+
Now armed with this information, we can start transforming the expressions in your printf
call.
Lets start with *(a + 0)
:
*(a + 0)
becomes a[0]
.a[0]
is an array, it will decay to a pointer to its first element, i.e. &a[0][0]
.So the first argument is equal to &a[0][0]
, i.e. a pointer to a[0][0]
.
Then lets take a + 0
.
a + 0
is equal to &*(a + 0)
.&*(a + 0)
becomes &a[0]
.&a[0]
is a pointer to the first element of a
, which happens to begin at the same position in memory as a[0][0]
.
This of course means that the pointer &a[0]
and &a[0][0]
both are pointing to the same locations, and the output would be equal. However the types of those two pointers are very different:
&a[0][0]
is a pointer to an int
, i.e. int *
&a[0]
is a pointer to an array of two int
elements, i.e. int (*)[2]