Related
In the code below, I declare an int array "a" and then a pointer to the entire array "ap":
int main(){
int a[3] = {10, 100, 1000};
int (*ap)[] = &a;
printf("%p\n", ap);
printf("%p\n", *ap);
printf("%d\n", **ap);
return 0;
}
The first print of ap shows that it is actually a pointer to the first value of the array. OK.
But in the second print, ap is dereferenced and, yet, the value is the same: pointer to the first value of the array.
It's only after a "double dereferencing" that I get the value of the first array element (10 in my example):
0x7fff193310cc
0x7fff193310cc
10
Why is that so?
Thanks a lot!
When you do *ap it's basically the same as doing *(&a), which is in essence plain a.
And as arrays decays to pointers to their first elements, plain a is the same as &a[0].
Now here's the interesting thing: The location of the array a and the location of the first element of a is the exact same location.
It's easy to see if we draw it out:
+------+------+------+
| a[0] | a[1] | a[2] |
+------+------+------+
^
|
&a[0]
|
&a
See how both &a[0] and &a are pointing to the same location?
But also remember that while both &a[0] and &a are pointing to the same location, they have very different types:
&a[0] will have the type int *
&a will have the type int (*)[3]
ap is a pointer to an array. In the program execution you observed, the array starts at address 7FFF193310CC16.
Since ap is a pointer to an array, *ap is an array. When an array is used in an expression other than as the operand of unary &, the operand of sizeof, or as a string literal used to initialize an array, it is automatically converted to a pointer to its first element. Therefore, *ap is converted to a pointer to (*ap)[0]. Naturally, the first element of an array starts in the same place the array does, since it is the first thing in the array, so its address is 7FFF193310CC16.
Since *ap is automatically converted to a pointer to (*ap)[0], **ap is the thing it points to, which is (*ap)[0], so it is the first element of the array, not a pointer.
Note that, to have behavior defined by the C standard when printing a pointer, you should convert it to void *:
printf("%p\n", (void *) ap);
printf("%p\n", (void *) *ap);
+------+---------+----------+
| 10 | 100 | 1000 |
+------+---------+----------+
^ ^ ^
| | |
| | +------+
| +---+ +--+
&ap | |
*ap **ap
&ap points to the base address of the array, its type is int (*)[3] (pointer to array with 3 int elements).
*ap points to the first element of the array, its type is int * (pointer to int).
**ap yields the value stored at the address of the first element, its type is int.
But in the second print, ap is dereferenced and, yet, the value is the same: pointer to the first value of the array.
The name of an array in C usually yields the address of the first element of the array.
However when you use the & operator in an expression such as &arrayname, it yields a pointer of type T(*)[size] instead of "pointer to the first element".
This means &a and a both have the same value, but they have completely different types, hence why ap and *ap also have the same value but different types.
You declared a pointer to an object of the type int[3].
int (*ap)[] = &a;
So the call of printf prints the address of the object of the type int[3]
printf("%p\n", ap);
Dereferencing the pointer you get an array of an unknown size due to this declaration of the pointer int (*ap)[]. Array designators used in expressions with rare exceptions are converted to pointers to their first elements. So the address of the first element of the array is outputted in this call of printf
printf("%p\n", *ap);
The addresses of the array itself and of its first element are equal each other.
This Q in C, how come the address of array, &array, &array[0] are the same? made me investigate on pointers and arrays: I confirm they are very similar but not identical.
&a[0] is the verbose form for a or &a. But what about the precedence of & vs [] ?
When I put parens the wrong way like (&a)[1] this changed the value, but not (&a)[0].
#include <stdio.h>
int main(){
int a[3] = {10,20,30};
int *b;
b = a;
printf("%p a\n", a);
printf("%p a+1\n", a+1);
printf("%p a+2\n\n", a+2);
printf("%p (&a)[0]\n", (&a)[0]);
printf("%p (&a)[1]\n", (&a)[1]);
printf("%p (&b)[0]\n", (&b)[0]);
printf("%p (&b)[1]\n\n",(&b)[1]);
printf("%p cast\n\n", (void **)((&a) + 1) );
int *c = a;
printf("%p\n", c);
printf("%p\n", &c);
return 0;
}
output:
0x7ffe18053c9c a
0x7ffe18053ca0 a+1
0x7ffe18053ca4 a+2
0x7ffe18053c9c (&a)[0]
0x7ffe18053ca8 (&a)[1]
0x7ffe18053c9c (&b)[0]
(nil) (&b)[1]
0x7ffe18053ca8 cast
0x7ffe18053c9c c (int *c = a)
0x7ffe18053c90 &c
I found (void **)((&a) + 1) to get the same result. Can't leave out a piece it seems. Suddenly &a does create a pointer, anonymously.
With index zero you get the base address (...c9c) both with a and its pointer b. But with index 1 strange things happen, and differently for b.
Is there a rule for this? I don't even know if I want to hear it! Is that what &a does besides being between a and &a[0]?
good A here by chux. His cast is simpler, but does it do the same?
difference between &array and &array[0] in C
From mentioned link I not only simplified my cast but now I think I know what it does and why the index or offset of 0 is special.
(void *)(&a + 0) is the cast. But why even cast? &a + 0 prints the base address. With offset + 1 the address advances a whole array size (not a pointer as I tried) - "one past the end". A second dimension. The base remains, but the offset changes.
For assignment:
int *c = a;
int (*d)[3] = &a; // with some help from the left...
int *e = (int *)&a; // ... or on the right side...
int *f = &a; // or with a warning, but working
So (&a) generates a new type, but not a pointer? New reference to the same spot. Address of = pointer to = array of.
TL;DR:
&a[0] returns the address of the first element of the array a. Since array in C is automatically decayed (converted) to the pointer to it's first element almost all the time, you will observe the same behavior by looking at a and &a[0]. But please be aware that there are cases when such conversion doesn't happen, the most obvious one would be using sizeof operator on the array.
(&a)[0] takes an address of a an array, and than treats the result as an array and tries to access it's first element. This is the same as just dereferencing resulting pointer, so (&a)[0] is the same as *(&a), which is the same as a. Than see above.
(&a)[1] is almost like previous, but it tries to access second element of the hypothetical array constructed from a pointer to array a. Since this is not actually an array, and there is no second element, this code is ill-formed, and triggers Undefined Behavior.
&a[0] is the verbose form for a or &a.
&a yields the same address value, but the type is different. This matters below.
But what about the precedence of & vs [] ?
Unary operators like & and * have lower precedence than postfix operators like [] and (), so &a[0] is parsed as &(a[0]) - you're taking the address of a[0].
When you write (&a)[0], you are indexing into the result of &a. The expression &a has type "pointer to 3-element array of int". The subscript operation a[i] is equivalent to the expression *(a + i) - given a starting address a, offset i elements (not bytes!) from that address and dereference the result1.
So the expression (&a)[0] is equivalent to *(&a + 0), which is equivalent to *&a, which is equivalent to a, which has type "3-element array of int". Unless this expression is the operand of the sizeof or unary & operators (as in &(&a)[0]), then it will "decay" to an expression of type "pointer to int" and the value of the expression will be the address of the first element of the array.
The expression (&a)[1] is equivalent to *(&a + 1). Like above, the type of this expression is "3-element array of int", and unless it is the operand of the sizeof or unary & operators, it "decays" to an expression of type "pointer to int" and the value of the expression will be the address of the first element of the array.
However...
Since &a has type "pointer to 3-element array of int", &a + 1 yields the address of the next 3-element array of int following a:
+---- +----+
| | 10 | a[0] <--- (&a)[0]
| +----+
a | 20 | a[1]
| +----+
| | 30 | a[2]
+---- +----+
| | ?? | <--- (&a)[1]
| +----+
?? | ?? |
| +----+
| | ?? |
+---- +----+
Pointer arithmetic takes the size of the pointed-to type into account. If p points to an object of type T, then p + 1 points to the next object of type T following it, not the next byte. Array subscripting is defined in terms of pointer arithmetic - that's why array expressions "decay" to pointers in the first place.
First of all, if you are a beginner, I'd advise to simply not worry about these things for now, as they can get confusing and have limited practical use. You have be warned :) Somewhat advanced answer follows.
&a[0] is the verbose form for a or &a
Not really.
&a[0] is the address of the first element of the array.
a is the array, which in most cases "decays" into a pointer to the first element whenever used in an expression. So it may or may not be the same as &a[0] depending on context.
&a is the address of "the array itself". This is the same address as of the first element, but a different type. This & is one of them few cases of expressions where the array doesn't "decay". You get a pointer to array here. Given int a[n] then &a gives you the type int (*)[n]. Which is not compatible with the pointer to the first element, they are different types.
Now (&a)[0] changes the meaning of things. In case of &a[0] the [] has highest precedence so we de-reference the array element before & is applied. But with the parenthesis, we get a pointer to array first, then we dereference that one. Which works, because C allows pointer arithmetic on these pointer types too. The result is pointer arithmetic done just as if we had an array of arrays. Which we don't, so you'll just get really weird results from this, each [] jumping ahead one int[3] array in memory rather than one int.
You could use pointers to array like this:
int a[2][3] = { {10,20,30} };
int (*p)[2][3] = &a;
printf("%d %d %d", (*p)[0][0], (*p)[0][1], (*p)[0][2]);
Though that's just hard to read for nothing gained. A trick is to drop the left-most dimension and thereby skip one level of de-referencing:
int a[2][3] = { {10,20,30} };
int (*p)[3] = a;
printf("%d %d %d", p[0][0], p[0][1], p[0][2]);
This question already has answers here:
How come an array's address is equal to its value in C?
(6 answers)
Closed 4 years ago.
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 ints, 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":
decay A to the address of the first element (or do nothing if you're defering/subscripting a pointer)
add to that the index scaled by 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):
Because the equivalence, *(a + 0) becomes a[0].
Because 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.
The expression a + 0 is equal to &*(a + 0).
Because the array/pointer equivalence &*(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:
The type of &a[0][0] is a pointer to an int, i.e. int *
The type of &a[0] is a pointer to an array of two int elements, i.e. int (*)[2]
I know array in C does essentially behaves like a pointer except at some places like (sizeof()). Apart from that pointer and array variables dont differ except in their declaration.
For example consider the two declarations:
int arr[] = {11,22,33};
int *arrptr = arr;
Now here is how they behave same:
printf("%d %d", arrptr[0], arr[0]); //11 11
printf("%d %d", *arrptr, *arr); //11 11
But here is one more place I found they differ:
//the outputs will be different on your machine
printf("%d %d", &arrptr, &arr); //2686688 2686692 (obviously different address)
printf("%d %d", arrptr, arr); //2686692 2686692 (both same)
Here the issue is with last line. I understand that arrptr contains the address of arr. Thats why the first address printed in last line is 2686692. I also understand that logically the address (&arr) and contents (arr) of arr should be same unlike arrptr. But then whats exactly that which (internally at implementation level) that makes this happen?
When the unary & operator is applied to an array, it returns a pointer to an array. When applied to a pointer, it returns a pointer to a pointer. This operator together with sizeof represent the few contexts where arrays do not decay to pointers.
In other words, &arrptr is of type int **, whereas &arr is of type int (*)[3]. &arrptr is the address of the pointer itself and &arr is the beginning of the array (like arrptr).
The subtle part: arrptr and &arr have the same value (both point to the beginning of the array), but are of a different type. This difference will show if you do any pointer arithmetic to them – with arrptr the implied offset will be sizeof(int), whereas with &arr it will be sizeof(int) * 3.
Also, you should be using the %p format specifier to print pointers, after casting to void *.
I know array in C does essentially behaves like a pointer except at some places like (sizeof()). Apart from that pointer and array variables dont differ except in their declaration.
This is not quite true. Array expressions are treated as pointer expressions in most circumstances, but arrays and pointers are completely different animals.
When you declare an array as
T a[N];
it's laid out in memory as
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[N-1]
+---+
One thing immediately becomes obvious - the address of the first element of the array is the same as the address of the array itself. Thus, &a[0] and &a will yield the same address value, although the types of the two expressions are different (T * vs. T (*)[N]), and the value may possibly adjusted based on type.
Here's where things get a little confusing - except when it is the operand of the sizeof or unary & operator, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
This means the expression a also yields the same address value as &a[0] and &a, and it has the same type as &a[0]. Putting this all together:
Expression Type Decays to Value
---------- ---- --------- -----
a T [N] T * Address of a[0]
&a T (*)[N] n/a Address of a
*a T n/a Value of a[0]
a[i] T n/a Value of a[i]
&a[i] T * n/a Address of a[i]
sizeof a size_t n/a Number of bytes in a
So why does this conversion rule exist in the first place?
C was derived from an earlier language called B (go figure). B was a typeless
language - everything was treated as basically an unsigned integer. Memory
was seen as a linear array of fixed-length "cells". When you declared an
array in B, an extra cell was set aside to store the offset to the first
element of the array:
+---+
a:| | ----+
+---+ |
... |
+-------+
|
V
+---+
| | a[0]
+---+
| | a[1]
+---+
...
+---+
| | a[N-1]
+---+
The array subscript operation a[i] was defined as *(a + i); that is, take the offset value stored in a, add i, and dereference the result.
When Ritchie was designing C, he wanted to keep B's array semantics, but couldn't figure out what to do with the explicit pointer to the first element, so he got rid of it. Thus, C keeps the array subscripting definition a[i] == *(a + i) (given the address a, offset i elements from that address and dereference the result), but doesn't set aside space for a separate pointer to the first element of the array - instead, it converts the array expression a to a pointer value.
This is why you see the same output when you print the values of arr and arrptr. Note that you should print out pointer values using the %p conversion specifier and cast the argument to void *:
printf( "arr = %p, arrptr = %p\n", (void *) arr, (void *) arrptr );
This is pretty much the only place you need to explicitly cast a pointer value to void * in C.
I have the following C program:
#include <stdio.h>
int main(){
int a[2][2] = {1, 2, 3, 4};
printf("a:%p, &a:%p, *a:%p \n", a, &a, *a);
printf("a[0]:%p, &a[0]:%p \n", a[0], &a[0]);
printf("&a[0][0]:%p \n", &a[0][0]);
return 0;
}
It gives the following output:
a:0028FEAC, &a:0028FEAC, *a:0028FEAC
a[0]:0028FEAC, &a[0]:0028FEAC
&a[0][0]:0028FEAC
I am not able to understand why are &a, a, *a - all identical. The same for a[0], &a[0] and &a[0][0].
EDIT:
Thanks to the answers, I've understood the reason why these values are coming out to be equal. This line from the book by Kernighan & Ritchie turned out to be the key to my question:
the name of an array is a synonym for the location of the initial element.
So, by this, we get
a = &a[0], and
a[0] = &a[0][0] (considering a as an array of arrays)
Intuitively, now the reason is clear behind the output. But, considering how pointers are implemented in C, I can't understand how a and &a are equal. I am assuming that there is a variable a in memory which points to the array(and the starting address of this array-memory-block would be the value of this variable a).
But, when we do &a, doesn't that mean taking the address of the memory location where the variable a was stored? Why are these values equal then?
They're not identical pointers. They're pointers of distinct types that all point to the same memory location. Same value (sort of), different types.
A 2-dimensional array in C is nothing more or less than an array of arrays.
The object a is of type int[2][2], or 2-element array of 2-element array of int.
Any expression of array type is, in most but not all contexts, implicitly converted to ("decays" to) a pointer to the array object's first element. So the expression a, unless it's the operand of unary & or sizeof, is of type int(*)[2], and is equivalent to &a[0] (or &(a[0]) if that's clearer). It becomes a pointer to row 0 of the 2-dimensional array. It's important to remember that this is a pointer value (or equivalently an address), not a pointer object; there is no pointer object here unless you explicitly create one.
So looking at the several expressions you asked about:
&a is the address of the entire array object; it's a pointer expression of type int(*)[2][2].
a is the name of the array. As discussed above, it "decays" to a pointer to the first element (row) of the array object. It's a pointer expression of type int(*)[2].
*a dereferences the pointer expression a. Since a (after it decays) is a pointer to an array of 2 ints, *a is an array of 2 ints. Since that's an array type, it decays (in most but not all contexts) to a pointer to the first element of the array object. So it's of type int*. *a is equivalent to &a[0][0].
&a[0] is the address of the first (0th) row of the array object. It's of type int(*)[2]. a[0] is an array object; it doesn't decay to a pointer because it's the direct operand of unary &.
&a[0][0] is the address of element 0 of row 0 of the array object. It's of type int*.
All of these pointer expressions refer to the same location in memory. That location is the beginning of the array object a; it's also the beginning of the array object a[0] and of the int object a[0][0].
The correct way to print a pointer value is to use the "%p" format and to convert the pointer value to void*:
printf("&a = %p\n", (void*)&a);
printf("a = %p\n", (void*)a);
printf("*a = %p\n", (void*)*a);
/* and so forth */
This conversion to void* yields a "raw" address that specifies only a location in memory, not what type of object is at that location. So if you have multiple pointers of different types that point to objects that begin at the same memory location, converting them all to void* yields the same value.
(I've glossed over the inner workings of the [] indexing operator. The expression x[y] is by definition equivalent to *(x+y), where x is a pointer (possibly the result of the implicit conversion of an array) and y is an integer. Or vice versa, but that's ugly; arr[0] and 0[arr] are equivalent, but that's useful only if you're writing deliberately obfuscated code. If we account for that equivalence, it takes a paragraph or so to describe what a[0][0] means, and this answer is probably already too long.)
For the sake of completeness the three contexts in which an expression of array type is not implicitly converted to a pointer to the array's first element are:
When it's the operand of unary &, so &arr yields the address of the entire array object;
When it's the operand of sizeof, so sizeof arr yields the size in bytes of the array object, not the size of a pointer; and
When it's a string literal in an initializer used to initialize an array (sub-)object, so char s[6] = "hello"; copies the array value into s rather than nonsensically initializing an array object with a pointer value. This last exception doesn't apply to the code you're asking about.
(The N1570 draft of the 2011 ISO C standard incorrectly states that _Alignof is a fourth exception; this is incorrect, since _Alignof can only be applied to a parenthesized type name, not to a expression. The error is corrected in the final C11 standard.)
Recommended reading: Section 6 of the comp.lang.c FAQ.
Because all expressions are pointing to the beginning of the array:
a = {{a00},{a01},{a10},{a11}}
a points to the array, just because it is an array, so a == &a[0]
and &a[0][0] is positioned at the first cell of the 2D array.
+------------------------------+
| a[0][0] <-- a[0] <-- a | // <--&a, a,*a, &a[0],&a[0][0]
|_a[0][1]_ |
| a[1][0] <-- a[1] |
| a[1][1] |
+------------------------------+
It is printing out the same values because they all are pointing to the same location.
Having said that,
&a[i][i] is of type int * which is a pointer to an integer.
a and &a[0] have the type int(*)[2] which indicates a pointer to an array of 2 ints.
&a has the type of int(*)[2][2] which indicates a pointer to a 2-D array or a pointer to an array of two elements in which each element is an array of 2-ints.
So, all of them are of different type and behave differently if you start doing pointer arithmetic on them.
(&a[0][1] + 1) points to the next integer element in the 2-D array i.e. to a[0][1]
&a[0] + 1 points to the next array of integers i.e. to a[1][0]
&a + 1 points to the next 2-D array which is non-existent in this case, but would be a[2][0] if present.
You know that a is the address of the first element of your array and according to the C standard, a[X] is equal to *(a + X).
So:
&a[0] == a because &a[0] is the same as &(*(a + 0)) = &(*a) = a.
&a[0][0] == a because &a[0][0] is the same as &(*(*(a + 0) + 0))) = &(*a) = a
A 2D array in C is treated as a 1D array whose elements are 1D arrays (the rows).
For example, a 4x3 array of T (where "T" is some data type) may
be declared by: T a[4][3], and described by the following
scheme:
+-----+-----+-----+
a == a[0] ---> | a00 | a01 | a02 |
+-----+-----+-----+
+-----+-----+-----+
a[1] ---> | a10 | a11 | a12 |
+-----+-----+-----+
+-----+-----+-----+
a[2] ---> | a20 | a21 | a22 |
+-----+-----+-----+
+-----+-----+-----+
a[3] ---> | a30 | a31 | a32 |
+-----+-----+-----+
Also the array elements are stored in memory row after row.
Prepending the T and appending the [3] to a we have an array of 3 elements of type T. But, the name a[4] is itself an array indicating that there are 4 elements each being an array of 3 elements. Hence we have an array of 4 arrays of 3 elements each.
Now it is clear that a points to the first element (a[0]) of a[4] . On the Other hand &a[0] will give the address of first element (a[0]) of a[4] and &a[0][0] will give the address of 0th row (a00 | a01 | a02) of array a[4][3]. &a will give the address of 2D array a[3][4]. *a decays to pointers to a[0][0].
Note that a is not a pointer to a[0][0]; instead it is a pointer to a[0].
Hence
G1: a and &a[0] are equivalent.
G2: *a, a[0]and &a[0][0] are equivalent.
G3: &a (gives the address of 2D array a[3][4]).
But group G1, G2 and G3 are not identical although they are giving the same result (and I explained above why it is giving same result).
This also means that in C arrays have no overhead. In some other languages the structure of arrays is
&a --> overhead
more overhead
&a[0] --> element 0
element 1
element 2
...
and &a != &a[0]
Intuitively, now the reason is clear behind the output. But, considering how pointers are implemented in C, I can't understand how a and &a are equal. I am assuming that there is a variable a in memory which points to the array(and the starting address of this array-memory-block would be the value of this variable a).
Well, no. There is no such thing as an address stored anywhere in memory. There is only memory allocated for the raw data, and that's it. What happens is, when you use a naked a, it immediately decays into a pointer to the first element, giving the impression that the 'value' of a were the address, but the only value of a is the raw array storage.
As a matter of fact, a and &a are different, but only in type, not in value. Let's make it a bit easier by using 1D arrays to clarify this point:
bool foo(int (*a)[2]) { //a function expecting a pointer to an array of two elements
return (*a)[0] == (*a)[1]; //a pointer to an array needs to be dereferenced to access its elements
}
bool bar(int (*a)[3]); //a function expecting a pointer to an array of three elements
bool baz(int *a) { //a function expecting a pointer to an integer, which is typically used to access arrays.
return a[0] == a[1]; //this uses pointer arithmetic to access the elements
}
int z[2];
assert((size_t)z == (size_t)&z); //the value of both is the address of the first element.
foo(&z); //This works, we pass a pointer to an array of two elements.
//bar(&z); //Error, bar expects a pointer to an array of three elements.
//baz(&z); //Error, baz expects a pointer to an int
//foo(z); //Error, foo expects a pointer to an array
//bar(z); //Error, bar expects a pointer to an array
baz(z); //Ok, the name of an array easily decays into a pointer to its first element.
As you see, a and &a behave very differently, even though they share the same value.