Pointers and Arrays : pointer constant - c

The book that I have says that An array name is a pointer constant.
So, I tried this :
int A[3][4] = {0};
A[0][0] = 1;
A[1][0] = 2;
A[2][0] = 3;
printf("A : %x\n", A);
printf("*A : %x\n", *A);
I expected the result of first printf is the address of A and the other is 1.
because, I thought array name is a pointer constant and the result would be *(address of A).
but, the results have same value; address of A.
do you know why? please give me some advice.

First, arrays are not pointers! Your book is wrong. Arrays and pointers share some operations, and array name can be automatically converted to a pointer to its first element in some cases, but remember they are different.
Second, in your code, A is a 2-dimensional array, i.e, an array of arrays. so *A, which is the same as A[0], is its first subarray. To access A[0][0], you need **A.

Arrays are not pointers. Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize another 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.
Given the declaration of A, all of the following are true:
Expression Type Decays to Value
---------- ---- --------- -----
A int [3][4] int (*)[4] &A[0][0]
*A int [4] int * &A[0][0]
&A int (*)[3][4] n/a &A[0][0]
A[i] int [4] int * &A[i][0]
*A[i] int n/a A[i][0]
&A[i] int (*)[4] n/a &A[i][0]
A decays to an expression of type "pointer to 4-element array of int". A[i] decays to an expression of type "pointer to int. The address of the first element of the array is the same as the address of the array itself, so the expressions
A
*A
&A
A[0]
&A[0]
&A[0][0]
all evaluate to the same value, but the types of the expressions will be different.

Array and pointers are not same.
The main difference is you cannot perform the pointer increment or decrement on the array name. You can think of array as constant pointer.
If you want to print the element of an array then try **A or *(A + i) in loop where i is the loop index

Don't confuse with array's and pointers! Both are different things! and also array is not a pointer! But array name represents base address of array!
In your code-
int A[3][4];
It is a 2D array so you need to dererence two times. If you dererence one time it will fetch the address of the array only. A, A[0], *A, &A, &A[0] all will represent the starting address of it.
Try -
printf("%d\n",**A);
or
printf("%d\n",A[0][0]);
or
printf("%d\n",*A[0]);

Related

How are 2d Array and pointers related? [duplicate]

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]

What is the difference between `a` and `*a` where `a` denotes a 2D array?

Pointers and arrays are closely associated with each other, so, if we have a 2-D array
int a[3][4]={
1,2,3,4,
5,6,7,8,
9,10,11,12};
Both
printf("%p\n", a);
and
printf("%p\n", *a);
print the same address. What I understand is maybe a is pointing to the base address of a while *a is pointing to the first sub-array of the two-dimensional array a.
So, what is the difference between the two?
What i get is may be a is pointing to the base address of a while *a
is pointing to the first sub-array of two-dimensional array a.
That's almost it.
a on its own decays to a pointer (of type int(*)[3]) to its first subarray when you pass it to printf.
*a designates a's first subarray (of type int[3]), and decays to a pointer (of type int*) to the first element of that subarray.
In the end, they share the same value because an array has the same address as its first element, recursively.
Given the declaration
int a[3][4];
the following are true:
Expression Type Decays to Value
---------- ---- --------- -----
a int [3][4] int (*)[4] Base address of array
&a int (*)[3][4] n/a Base address of array
*a int [4] int * Base address of first subarray
(equivalent to a[0])
a[i] int [4] int * Base address of i'th subarray
&a[i] int (*)[4] n/a Base address of i'th subarray
*a[i] int n/a Value of a[i][0]
a[i][j] int n/a Value of a[i][j]
&a[i][j] int * n/a Address of a[i][j]
The address of the first element of the array is the same as the address of the array itself, so the expressions a, &a, *a, a[0], &a[0], and &a[0][0] all yield the same value (the address of the first element of the array), but they will have different types (int (*)[3][4] vs. int (*)[4] vs. int *).
Edit
Except when it is the operand of the sizeof or unary & operators, 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.
So the expression *a is equivalent to a[0]; both expressions have type "4-element array of int". Since they're not operands of the sizeof or unary & operators, those expressions "decay" to type "pointer to int".
That's what the "Decays to" column shows.
See section 6.3.2.1 of the online C 2011 language standard.
a is a 2D array of size 3x4. That means:
sizeof(a) == 3*4*sizeof(int)
a[i] is a 1D array of size 4 (for 0<=i<3)
a[i][j] is an int ( 0<=i<3 and 0<=j<4)
when used as a pointer a decays to &a[0]
*a is a[0]: it is a 1D array of size 4. That means :
sizeof(*a) == 4*sizeof(int)
(*a)[i] is an int (0<=i<4)
when used as a pointer *a decays to &a[0][0]
So a and *a are different objects that decay to pointers to the same address which is also &a. But a decays to a pointer to int[4], whereas *a decays to a pointer to int (thanks to undur_gongor for noticing).
The difference between the pointers (that the arrays decay to) is not the address but the type.
As you have seen, both pointers point to the same address. But a points to sub-arrays whereas *a points to ints.
This makes a difference, e.g. when doing address arithmetic. a + 1 is something different than *a + 1.
It is very simple. *a is an array of integers. In your case *a in nothing but the array [1,2,3,4]. Individual elements of this array can be accessed by (*a)[i], where i is the index of the required element.
And a is pointer to this array of integers. a[0] will give you the first array (it is same as *a), a[1] will give you the second array(which is [5,6,7,8]).
For example if you want to access 5th element(which is 5), it will a[1][0].

Address for a 2/3d array in c

So, if I have this in C,
int a[50];
and I use a, it actually points to the address of the first element of the array.
But, if I have int b[10][10] , and I use b or b[0] , to what it will point to?
Secondly, if I do,
`&a[0]`
this basically gives me the address of the first element, but if I do, &a , what does this mean actually? And also operations like, &a + 1 . Basically, are we calculating the address of the address of the first element of the array?
Or for that matter if I have a 3d array, int c[10][10][10], then what does, c means? To which element will it point to? Or, if I do, c[0]+1 , to what part of the array will this point to?
Thanks!
If you have an array such as
int a[X][Y];
Then using just a will have it decay to a pointer to the first element in the first array.
The expression &a[0] will return a pointer to the first array, hich then decays to a pointer to the first element. So both a and &a[0] (as well as plain a[0] and of course &a[0][0]) will all point to the same thing. The difference is in the types:
a is an array of arrays of integers, which decays to a pointer to an array of integers
a[0] is an array of integers, which decays to a pointer to an integer
&a[0] is a pointer to an array of integers
&a is a pointer to an array of arrays of integers
&a[0][0] is a pointer to an integer
All the expressions above, will either point to, or decay to a pointer to, the very same element: The first integer in the first array (i.e a[0][0]).
For completeness sake:
a[0][0] is an integer, the very first in the very first array.
Since &a is a pointer to an array of array of integers, adding one (i.e &a + 1) will add the size of the array of array integers, in other words the expression returns a pointer to a + sizeof(a), which is a pointer to one beyond the end of a. In more other words, it's the same as (&a)[1].
More curiosity: For a generic pointer or array p, doing p[x] is the same as doing p + x. And now for the weird part: Due to the communicative nature of addition, the expression p + x is equal to x + p, which in turn leads to p[x] is equal to x[p].
For the dereferencing:
*a decays a to a pointer to an array of arrays of integers, so *a is an array of integers (i.e. a[0])
*a[0] since the indexing operator has higher precedence than the dereference operator, you first have a[0] which is an array of integers, the dereference then decays it to a pointer to the first element of that array which it the dereferences (i.e. it's the same as a[0][0])
Except when it is the operand of the sizeof or unary & operator, or is a string literal being used to initialize an 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 M-element array.
Given the following declaration:
T a[N][M];
the expression a has type "N-element array of M-element arrays of T"; by the rule above, it will decay to an expression of type "pointer to M-element array of T", or T (*)[M], and the value will be the address of the first element of that array.
The following table shows the types and meanings of various expressions involving a:
Expression Type Decays to Meaning
---------- ---- --------- -------
a T [N][M] T (*)[M] Points to first M-element array
&a T (*)[N][M] Points to entire NxM array
*a T [M] T * Points to first element of first array
a[i] T [M] T * Points to first element of i'th array
&a[i] T (*)[M] Points to i'th M-element array
*a[i] T Value of first element of i'th array
a[i][j] T Value of j'th element of i'th array
&a[i][j] T * Points to j'th element of i'th array
Since the address of the first element is the same as the address of the array itself, all of the expressions a, *a, &a, a[0], &a[0], and &a[0][0] yield the same value, but the types of the expressions are different.
This pattern extends to higher-dimensioned arrays. I'd draw out an example for a 3-d array, but I'm typing this on an iPad, which has already slowed me down considerably.

Problems with 2 D arrays

I wrote the following code in C:
#include<stdio.h>
int main()
{
int a[10][10]={1};
//------------------------
printf("%d\n",&a);
printf("%d\n",a);
printf("%d\n",*a);
//-------------------------
printf("%d",**a);
return 0;
}
With the above 3 printf statements I got the same value. On my machine it's 2686384. But with the last statement I got 1.
Isn't it something going wrong? These statements mean:
The address of a is 2686384
The value stored in a is 2686384
the value that is stored at address of variable pointed by a (i.e. at 2686384) is 2686384.
This means a must be something like a variable pointing towards itself...
Then why is the output of *(*a) 1? Why isn't it evaluated as *(*a)=*(2686384)=2686384?
#include<stdio.h>
int main()
{
// a[row][col]
int a[2][2]={ {9, 2}, {3, 4} };
// in C, multidimensional arrays are really one dimensional, but
// syntax alows us to access it as a two dimensional (like here).
//------------------------
printf("&a = %d\n",&a);
printf("a = %d\n",a);
printf("*a = %d\n",*a);
//-------------------------
// Thing to have in mind here, that may be confusing is:
// since we can access array values through 2 dimensions,
// we need 2 stars(asterisk), right? Right.
// So as a consistency in this aproach,
// even if we are asking for first value,
// we have to use 2 dimensional (we have a 2D array)
// access syntax - 2 stars.
printf("**a = %d\n", **a ); // this says a[0][0] or *(*(a+0)+0)
printf("**(a+1) = %d\n", **(a+1) ); // a[1][0] or *(*(a+1)+0)
printf("*(*(a+1)+1) = %d\n", *(*(a+1)+1) ); // a[1][1] or *(*(a+1)+1)
// a[1] gives us the value on that position,
// since that value is pointer, &a[i] returns a pointer value
printf("&a[1] = %d\n", &a[1]);
// When we add int to a pointer (eg. a+1),
// really we are adding the lenth of a type
// to which pointer is directing - here we go to the next element in an array.
// In C, you can manipulate array variables practically like pointers.
// Example: littleFunction(int [] arr) accepts pointers to int, and it works vice versa,
// littleFunction(int* arr) accepts array of int.
int b = 8;
printf("b = %d\n", *&b);
return 0;
}
An expression consisting the the name of an array can decay to a pointer to the first element of the array. So even though a has type int[10][10], it can decay to int(*)[10].
Now, this decay happens in the expression *a. Consequently the expression has type int[10]. Repeating the same logic, this again decays to int*, and so **a is an int, which is moreover the first element of the first element of the array a, i.e. 1.
The other three print statements print out the address of, respectively, the array, the first element of the array, and the first element of the first element of the array (which are of course all the same address, just different types).
First, a word on arrays...
Except when it is the operand0 of the sizeof, _Alignof, or unary & operators, or is a string literal being used to initialize another 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 in the array.
The expression &a has type "pointer to 10-element array of 10-element array of int", or int (*)[10][10]. The expression a has type "10-element array of 10-element array of int", which by the rule above decays to "pointer to 10-element array of int", or int (*)[10]. And finally, the expression *a (which is equivalent to a[0]) has type "10-element array of int", which again by the rule above decays to "pointer to int".
All three expressions have the same value because the address of an array and the address of its first element are the same: &a[0][0] == a[0] == *a == a == &a. However, the types of the expressions are different, which matters when doing pointer arithmetic. For example, if I have the following declarations:
int (*ap0)[10][10] = &a;
int (*ap1)[10] = a;
int *ip = *a;
then ap0++ would advance ap0 to point to the next 10x10 array of int, ap1++ would advance ap1 to pointer to the next 10-element array of int (or a[1]), and ip++ would advance ip to point to the next int (&a[0][1]).
**a is equivalent to *a[0] which is equivalent to a[0][0]. which is the value of the first element of a and has type int and the value 1 (note that only a[0][0] is initialized to 1; all remaining elements are initialized to 0).
Note that you should use %p to print out pointer values:
printf("&a = %p\n", &a);
printf(" a = %p\n", a);
printf("*a = %p\n", *a);
First of all, if you want to print out pointer values, use %p - if you're on a 64 bit machine int almost certainly is smaller than a pointer.
**a is double dereferencing what's effectively a int**, so you end up with what the first element of the first sub-array is: 1.
If you define a as T a[10] (where T is some typedef), then a simple unadorned a means the address of the start of the array, the same as &a[0]. They both have type T*.
&a is also the address of the start of the array, but it has type T**.
Things become trickier in the presence of multi-dimensional arrays. To see what is happening, it is easier to break things down into smaller chunks using typedefs. So, you effectively wrote
typedef int array10[10];
array10 a[10];
[Exercise to reader: What is the type of a? (it is not int**)]
**a correctly evaluates to the first int in the array a.
From C99 Std
Consider the array object defined by the declaration
int x[3][5];
Here x is a 3 × 5 array of ints; more precisely, x is an array of three element objects, each of which is an array of five ints. In the expression x[i], which is equivalent to (*((x)+(i))), x is first converted to a pointer to the initial array of five ints. Then i is adjusted according to the type of x, which conceptually entails multiplying i by the size of the object to which the pointer points, namely an array of five int objects. The results are added and indirection is applied to yield an array of five ints. When used in the expression x[i][j], that array is in turn converted to a pointer to the first of the ints, so x[i][j] yields an int.
so,
Initial array will be x[0][0] only.
all x, &x and *x will be pointing to x[0][0].
No, there's nothing wrong with your code. Just they way you are thinking about it... The more I think about it the harder I realize this is to explain, so before I go in to this, keep these points in mind:
arrays are not pointers, don't think of them that way, they are different types.
the [] is an operator. It's a shift and deference operator, so when I write printf("%d",array[3]); I am shifting and deferencing
So an array (lets think about 1 dimension to start) is somewhere in memory:
int arr[10] = {1};
//Some where in memory---> 0x80001f23
[1][1][1][1][1][1][1][1][1][1]
So if I say:
*arr; //this gives the value 1
Why? because it's the same as arr[0] it gives us the value at the address which is the start of the array. This implies that:
arr; // this is the address of the start of the array
So what does this give us?
&arr; //this will give us the address of the array.
//which IS the address of the start of the array
//this is where arrays and pointers really show some difference
So arr == &arr;. The "job" of an array is to hold data, the array will not "point" to anything else, because it's holding its own data. Period. A pointer on the other hand has the job to point to something else:
int *z; //the pointer holds the address of someone else's values
z = arr; //the pointer holds the address of the array
z != &z; //the pointer's address is a unique value telling us where the pointer resides
//the pointer's value is the address of the array
EDIT:
One more way to think about this:
int b; //this is integer type
&b; //this is the address of the int b, right?
int c[]; //this is the array of ints
&c; //this would be the address of the array, right?
So that's pretty understandable how about this:
*c; //that's the first element in the array
What does that line of code tell you? if I deference c, then I get an int. That means just plain c is an address. Since it's the start of the array it's the address of the array, thus:
c == &c;

How does C retrieve the address of a row for a 2d array

Can someone explain to me how C retrieves the correct memory address for a row when you only use one subscript to access a 2d array?
Example -
int array2D[2][2] = {1,2,3,4};
printf ( "starting address of row2 = %p" , array2D[1]);
I understand that when subscripting in C that what is actually going on is pointer addition so for a 1d array the array name points to element 0. In this case if I had wanted element 1 the compiler would take the starting address (say 4000) and add 4 to it (assuming a 4 bit int) so that what is returned is the item at memory address 4004.
My understanding is that when you populate a 2d array, as in my example, they are allocated sequentially so I would have
1 2
3 4
at addresses
4000 4004
4008 4012
So how does C work out that in this case array2D[1] should point to 4008 and not 4004? Does it run a sizeof() operator or have I misunderstood a fundamental here?
Thanks in advance
C knows how long each row is, so it does the multiplication to find the row.
int x[][3] = {{1,2,3},{4,5,6}};
then &x[1][0] is &x[0][0] plus 3 * sizeof(int).
That's why in a multidimensional C array declaration, all but the first dimension must be specified.
Pointer arithmetic depends on the type of the element being pointed to. Given a pointer p to type T, p + 1 points to the next element of type T, not necessarily the next byte following p. If T is char, then p + 1 points to the next char object after p, which starts at the byte immediately following p; if T is char [10], then p + 1 points to the next 10-element array of char after p, which starts at the 10th byte following p.
The type of the expression array2d in is "2-element array of 2-element array of int", which "decays" to type "pointer to 2-element array of int", or int (*)[2]1. Thus the expression array2d[1] is interpreted as *(array2d + 1). Since array2d points to an object of type int [2], array2d + 1 points to the next 2-element array of int following array2d, which is 2 * sizeof int bytes away from array2d.
1. Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted to an expression of type "pointer to T" and its value will be the address of the first element in the array.
This is going to be a bit long-winded, but bear with me still.
Array subscription is just a shorthand: (p[N]) equals (*(p + N)) in all contexts for pointer types (both are invalid expressions for void*, though).
Now, if p is an array type, it would decay to a pointer type in an expression like (*(p + N)); an int[2][2] would decay into a pointer of type (*)[2] (i.e. a pointer to an int[2]).
Pointer arithmetic takes types into account; we need to convert things to char* to visualize what the compiler does to us:
T *p;
p[N] equals *(p + N) equals *(T*)((unsigned char*)p + N * sizeof *p)
Now, if T were an int[2] (to equal the situation we described above), then sizeof *p would be sizeof(int[2]), i.e. 2 * sizeof(int).
This is how subscription works in so-called multidimensional arrays.
sizeof(array2D[1]) == 8;
if array2D address is 4000;
so array2D[1] address is 4000+sizeof(array2D[1]) == 4000+8;

Resources