In C I learnt that the name of array is a pointer to the first index, but why I can't write the following:
int mat[5][4];
*mat=1;
In C I learnt that the name of array is a pointer to the first index
This is not exactly so.
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
This declaration
int mat[5][4];
declares a two-dimensional array. So the array designator used in expressions (with rare exceptions listed in the quote) is implicitly converted to pointer to its first element that is to pointer of the type int ( * )[4].
Dereferencing the pointer like in this statement
*mat=1;
you will get an array lvalue of the type int[4]. And as a result you are trying to assign a one-dimensional array with the scalar integer value 1. But arrays do not have the assignment operator. They are non-modifiable lvalues.
Instead you could write for example
**mat = 1;
In this case the expression **mat yields a scalar lvalue of the type int (the first scalar element of the type int of the first "row"/element of the array m).
Here is a demonstrative program.
#include <stdio.h>
int main(void)
{
int mat[5][4];
**mat = 1;
printf( "**mat is equal to %d and equivalent to mat[0][0] that is also equal to %d\n",
**mat, mat[0][0] );
}
Its output is
**mat is equal to 1 and equivalent to mat[0][0] that is also equal to 1
In fact all these expressions used in the printf calls
printf( "%d\n", **mat );
printf( "%d\n", ( *mat )[0] );
printf( "%d\n", *mat[0] );
printf( "%d\n", mat[0][0] );
yield the same element of the array.
2D arrays are essentially 1D arrays of 1D arrays. *mat is equivalent to mat[0] and gives you the first element of the array, which is itself a 1D array (int[4]).
You can't assign a number to an array.
name of array is a pointer to the first index
I often see this explanation, but the difference between "array" and "name of array" is moot.
A better explanation is that an array can be automatically converted to the pointer to its first element.
You can use the array name as a pointer to the first element of your array (even though there are more elegant solutions).
In your case the first element of your array is another array, so *mat has the type int[4] (int array with 4 elements) and you can't assign 1 (integer) to it.
If you want to set the first integer of your two dimensional array, you could use
**mat = 1;
But I would highly recommend using explicit indexes like
mat[0][0] = 1;
Related
I am new to C and spend some hours with arrays and pointers.
Now, I got a specific question I can't really answer by myself.
What are the two variables mat_ptr and ptr which I create in this example and why are they different?
To me it looks like each is an array of pointers storing the pointers to the beginning of the 3 rows of the matrix.
int matrix[3][3] = {{0,1,2},
{0,1,2},
{0,1,2}};
int (*mtr_ptr)[3] = matrix;
int *ptr[3];
for (int i=0; i< 3; i++)
{
ptr[i]=matrix[i];
}
I the end, I think ptr ist of type int ** but what exactly is ptr_ptr?
A pointer to an array of size 3 which is an array of arrays?
This code:
int matrix[5][5] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}}
is missing a semicolon at the end. When that is fixed, it defines matrix to be a 5×5 array (formally an array of 5 arrays of 5 int) in which rows 0 to 2 are initialized with four values (0, 1, 2, and 3), leaving element 4 implicitly initialized to zero. Rows 3 to 4 are implicitly initialized to zero.
This code:
int (*mtr_ptr)[3] = matrix;
defines mtr_ptr to be a pointer to an array of 3 int and attempts to initialize it with matrix. Since matrix is an array, it will be automatically converted to a pointer to its first element, matrix[0]. Thus, we have a pointer to an array of 5 int. This is not a proper type to initialize a pointer to an array of 3 int, so the compiler will complain.
If matrix were defined as int matrix[5][3] or int matrix[3][3], and the then-excess initializers were removed, then the types in int (*mtr_ptr)[3] = matrix; would match, and the compiler would not complain.
This code:
int *ptr[3];
for (int i=0; i< 3; i++)
{
ptr[i]=matrix[i];
}
defines an array of 3 pointers to int and assigns them values from matrix[i]. Since each matrix[i] is an array, it will be converted to a pointer to its first element, matrix[i][0]. So each ptr[i] will be assigned to point to matrix[i][0].
First things first. There is a semi-colon missing from the definition of matrix, corrected below:
int matrix[5][5] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}};
Regarding the definition of mtr_ptr below:
int (*mtr_ptr)[3] = matrix;
mtr_ptr is declared as a pointer to an array length 3 of ints. However, it is being initialized with an incompatible pointer. matrix is an array length 5 of array length 5 of int. In the assignment expression above, matrix is converted to a pointer to the first element. The elements of matrix have type array length 5 of int, so matrix is converted to a pointer to array length 5 of int with value equivalent to &matrix[0]. Therefore, mtr_ptr should be defined as:
int (*mtr_ptr)[5] = matrix;
or equivalently:
int (*mtr_ptr)[5] = &matrix[0];
Regarding the code below:
int *ptr[3];
for (int i=0; i< 3; i++)
{
ptr[i]=matrix[i];
}
ptr is defined as an array length 3 of pointers to int. matrix[i] is an array length 5 of int. In the assignment expression, it is converted to a pointer to its first element. The element type is int, so matrix[i] is converted to a pointer to int equivalent to &matrix[i][0]. Therefore, the assignement ptr[i]=matrix[i]; is equivalent to ptr[i]=&matrix[i][0];.
These declarations
int matrix[5][5] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}}
int (*mtr_ptr)[3] = matrix;
do not make a sense. It seems you mean at least the following declarations
int matrix[3][4] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}};
int (*mtr_ptr)[4] = matrix;
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array
object has register storage class, the behavior is undefined.
So the array matrix used in expressions with rare exceptions is converted to pointer to its initial element of the type int ( * )[4].
Thus in this declaration
int (*mtr_ptr)[4] = matrix;
the pointer mtr_ptr is initialized by the address of the first "row" of the array matrix.
On the other hand, the expression matrix[i] is the i-th element of the array matrix that has the type int[4]. Such an array used in expression is in turn converted to pointer to its first element of the type int *.
In this line
int *ptr[3];
there is declared an array of three elements of the type int *.
And in this loop
for (int i=0; i< 3; i++)
{
ptr[i]=;
}
each element of the array of pointers is assigned by the address of the first element of the corresponding "row" of the array matrix that ("row") used in the expression matrix[i] is implicitly converted to pointer.
Unless 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" 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.
In the line
int (*mtr_ptr)[3] = matrix;
the expression matrix has type "5-element array of 5-element array of int". Since matrix is not the operand of the sizeof or unary & operators, it "decays" to type "pointer to 5-element array of int", or int (*)[5], and its value is the address of the first element of matrix (same as &matrix[0]).
int (*)[5] and int (*)[3] are different, incompatible types, so the compiler should yak at you on that initializer. You should declare mtr_ptr as
int (*mtr_ptr)[5] = matrix;
Remember that mtr_ptr is a pointer, not an array - it stores a single value.
That single value has type int (*)[5].
By contrast, the expression matrix[i] has type "5-element array of int"; by the same rule above, that expression "decays" to type "pointer to int", or int *. Since each element of your ptr array is storing an int *, you declare it as
int *ptr[3];
In this case ptr is an array, and each element simply stores an int *. That pointer may be the address of the first element matrix[i], or it may be a pointer to a single int object that isn't part of a larger array, or it may be something else:
ptr[0] = matrix[0];
ptr[1] = &i;
...
The size of ptr does not depend on the size of matrix beyond whether or not you want to store the address of all rows or matrix or just a subset.
As we know, when passing arrays as function arguments, only the first dimension's size can be empty, the others must be specified.
void my_function(int arr[5][10][15]); // OKAY!
void my_function(int arr[][10][15]); // OKAY!
void my_function(int arr[][][15]); // WRONG!!!
void my_function(int arr[][][]); // WRONG!!!
What is logic behind this? Could someone explain the main reason?
Passing arrays to a function is an illusion: arrays instantly decay to a pointer when you use them. That is, with this example:
int foo[10];
foo[1];
the way this is interpreted by the compiler, in foo[1], foo is first transformed to a pointer to element 0 of foo, and then subscripting is the same as *(pointer_to_foo + 1).
However, this is only possible with the first dimension of an array. Suppose this:
int foo[5][5];
This puts 25 integer elements contiguously in memory. It is not the same as int** foo, and not convertible to int** foo, because int** foo represents some number of pointers to some number of pointers, and they don't have to be contiguous in memory.
While it's legal to use an array type as a function parameter, the compiler builds it identically to if you had specified a pointer to the array's element type:
int foo(int bar[10]); // identical to `int foo(int* bar)`
But what happens if you pass a multi-dimensional array?
int foo(int bar[5][5]); // identical to what?
Only the first dimension of the array can undergo decay. The equivalent signature for int foo here would be:
int foo(int (*bar)[5]); // identical to `int foo(int bar[5][5])`
where int (*bar)[5] is a pointer to an array of 5 integers. Stacking up more dimensions doesn't change the idea, only the first dimension will decay, and the other dimensions need to have a known size.
In other words, you can skip the first dimension because the compiler doesn't care about its size, as it instantly decays. However, subsequent dimensions do not decay at the call site, so you need to know their size.
Arrays in C and C++ are stored contiguously in memory. In the case of a multidimensional array, this means you have an array of arrays.
When you traverse an array, you don't necessarily need to know how many elements are in the array. You do however need to know the size of each element of the array so you can jump to the correct element.
That's why int arr[][][15] is incorrect. It says that arr is an array of unknown size elements are of type int [][15]. But this means you don't know how big each array element is, so you don't know where each array element it in memory.
Starting with two bits of standardese:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
...
6.7.6.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
C 2011 online draft
Lovely. What does all that mean?
Let's start with the declaration of an array of some arbitrary type T:
T arr[N];
The type of the expression arr is "N-element array of T" (T [N]). Unless that expression is the operand of the unary &, sizeof, or _Alignof operators as specified in 6.3.2.1/3 above, then the type of the expression is converted ("decays") to "pointer to T" (T *) and the value of the expression is the address of the first element - i.e., &arr[0].
When we pass arr as a parameter to a function, as in
foo( arr );
what foo actually receives is a pointer, not an array, and you would write the function prototype as
void foo( T *arr )
Or...
As a notational convenience, C allows you to declare the formal parameter using array notation:
void foo( T arr[N] )
or
void foo( T arr[] )
In this case, both T arr[N] and T arr[] are "adjusted" to T *arr as per 6.7.6.3/7 above, and all three forms declare arr as a pointer (this is only true for function parameter declarations).
That's easy enough to see for 1D arrays. But what about multidimensional arrays?
Let's replace T with an array type A [M]. Our declaration now becomes
A arr[N][M]; // we're creating N M-element arrays.
The type of the expression arr is "N-element array of M-element arrays of A" (A [M][N]). By the rule above, this "decays" to type pointer to *M-element array* ofA" (A (*)[M]`). So when we call
foo( arr );
the corresponding prototype isvoid foo( A (*arr)[M] )1
which can also be written as
void foo( A arr[N][M] )
or
void foo( A arr[][M] );
Since T arr[N] and T arr[] are adjusted to T *arr, then A arr[N][M] and A arr[][M] are adjusted to A (*arr)[M].
Let's replace A with another array type, R [O]:
R arr[N][M][O];
The type of the expression arr decays to R (*)[M][O], so the prototype of foo can be written as
void foo( R (*arr)[M][O] )
or
void foo( R arr[N][M][O] )
or
void foo( R arr[][M][O] )
Are you starting to see the pattern yet?
When a multi-dimensional array expression "decays" to a pointer expression, only the first (leftmost) dimension is "lost", so in a function prototype declaration, only the leftmost dimension my be left blank.
Since the subscript [] operator has a higher precedence than the unary * operator, A *arr[M] would be interpreted as "M-element array of pointers to A", which is not what we want. We have to explicitly group the * operator with the identifier to properly declare it as a pointer to an array.
What is the reason, that the first index in the function void transposeMatrix(int a[][arraySize]) is empty?
Because what transposeMatrix receives really isn't a 2D array, but rather a pointer to a 1D array.
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" 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.
If you declare an array as
int arr[N][M];
and pass it to a function
void foo( arr );
then the expression arr is converted from type "N-element array of M-element array of int" to "pointer to M-element array of int" (int (*)[M]).
In a function parameter declaration, T a[N] and T a[] are "adjusted" to T *a - IOW, all three declare a as a pointer to T (this is only true for function parameter declarations).
Thus,
void transposeMatrix(int a[][arraySize])
is equivalent to
void transposeMatrix(int (*a)[arraySize])
Using a[][M] rather than (*a)[M] is a notational convenience (similar to using p->m instead of (*p).m for accessing struct and union members through pointers).
Because of how array indexing works, you can index into a like any other 2D array. Remember that the subscript operation a[i] is defined as *(a + i) - given a starting address a, find the address of the i'th element (not byte) following a and dereference the result. So:
(*a)[i] == (*(a + 0))[i] == (a[0])[i] == a[0][i]
meaning
(*(a + j))[i] == (a[j])[i] == a[j][i]
When you have an array a of n objects of type foo, the compiler can calculate the location of element a[i] by adding i times the size of a foo object to the starting address of a.
Note that this calculation involves only the size of foo and the subscript i. It does not involve the number of elements n. The compiler does not need to know the number of elements in order to calculate where an element is.
You would need to know the number of elements in an array to avoid going beyond the end of an array. However, in C, it is not the compiler’s job to guard against going beyond the end of the array. The author of the function is responsible for doing that. So, for this purpose, the compiler does not need to know how many elements are in an array.
Now suppose each foo object is itself an array of m objects of type bar. The compiler does need to know the size of a foo object. How big is a foo? Since a foo is an array of m objects of type bar, its size is m times the size of bar. So, to know how big a foo is, the compiler needs to know how many elements in in the array.
Thus, when passing an int a[FirstDimension][SecondDimension], the compiler does not need to know FirstDimension but does need to know SecondDimension.
Kernighan & Ritchie 2nd ed. says:
The correspondence between indexing and pointer arithmetic is very close. By definition, the value of a variable or expression of type array is the address of element zero of the array. Thus after the assignment
pa = &a[0];
pa and a have identical values. Since the name of an array is a synonym for the location of the initial element, the assignment pa=&a[0] can also be written as
pa = a;
If a and pa are identical, then why this code:
#include <stdio.h>
int main()
{
char a[] = "hello";
char *pa = a;
printf("Array: %ld\n", sizeof(a));
printf("Pointer: %ld\n", sizeof(pa));
}
Outputs this:
Array: 6
Pointer: 8
Reference to an authoritative source would be much appreciated.
Two objects can have the same address but their sizes can be different.
From the C Standard (6.5.3.4 The sizeof and alignof operators)
2 The sizeof operator yields the size (in bytes) of its operand, which
may be an expression or the parenthesized name of a type. The size is
determined from the type of the operand....
Consider the following example
#include <stdio.h>
int main( void )
{
struct A
{
char c;
int x;
} a;
printf( "object a:\taddress - %p size - %zu\n",
&a, sizeof( a ) );
printf( "object a.c:\taddress - %p size - %zu\n",
&a.c, sizeof( a.c ) );
}
The program output is
object a: address - 0x7fff164e16d0 size - 8
object a.c: address - 0x7fff164e16d0 size - 1
As it is seen the object a of type struct A and its data member c of type char have the same address but different sizes.
As for arrays then a pointer is an object that stores an address of other object. To store an address of other object it is enough to allocate for example 4 or 8 bytes of memory for the pointer depending on the used system.
As for arrays then they are named extents of memory. Arrays do not store addresses. They store their own elements (that of course can be pointers).
An array name used in expressions is converted to pointer to its first element.
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
In this quote there is listed when an array is not converted to a pointer to its first element. For example when an array is the operand of sizeof operator.
If to return to your program
int main()
{
char a[] = "hello";
char *pa = a;
printf("Array: %ld\n", sizeof(a));
printf("Pointer: %ld\n", sizeof(pa));
}
then in this statement
char a[] = "hello";
string literal "Hello" that has type char[6] is not converted to a pointer.
However in this statement
char *pa = a;
array a is converted to pointer to its first element.
And in this statement
printf("Array: %ld\n", sizeof(a));
array a is not converted to a pointer because it is the operand of the sizeof operator.
However if you used an expression in the sizeof operator for example like this
sizeof( a + 0 )
then you would get a pointer and correspondingly the sizeof would return the size of the pointer instead of the size of the array
They do indeed have identical values. But that doesn't mean they are the same thing.
a is still a fixed-sized array. pa is still a pointer.
sizeof is one operator that recognises this difference.
Your array has 6 elements of size char (sizeof(char) is defined by the standard to be 1). (The 6th element is the string null terminator).
sizeof(char*) is 8 on your system. It's probably 64 bit.
Arrays are not pointers. The array name decays to a pointer to its first element in many cases, but sizeof is one of the few exceptions.
C11 §6.3.2.1 Lvalues, arrays, and function designators
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue.
char a[] = "hello";
char *pa = a;
Here sizeof(a) will give size of array a . And sizeof(pa) will give size of pointer pa.Both are different .
Also as in function arguments array decays to pointer but this one is exception along with &.
Also while printing type size_t you should use specifier %zu (as specified in ANSI C99).
a and pa are not identical. Always remember: Arrays are not pointers. When used in an expression arrays are converted to pointer to its first element with some exception including as an operand of sizeof operator.
sizeof(a) will give the size of array while sizeof(pa) will give the size of pointer.
My understanding was that arrays were simply constant pointers to a sequence of values, and when you declared an array in C, you were declaring a pointer and allocating space for the sequence it points to.
But this confuses me: the following code:
char y[20];
char *z = y;
printf("y size is %lu\n", sizeof(y));
printf("y is %p\n", y);
printf("z size is %lu\n", sizeof(z));
printf("z is %p\n", z);
when compiled with Apple GCC gives the following result:
y size is 20
y is 0x7fff5fbff930
z size is 8
z is 0x7fff5fbff930
(my machine is 64 bit, pointers are 8 bytes long).
If 'y' is a constant pointer, why does it have a size of 20, like the sequence of values it points to? Is the variable name 'y' replaced by a memory address during compilation time whenever it is appropiate? Are arrays, then, some sort of syntactic sugar in C that is just translated to pointer stuff when compiled?
Here's the exact language from the C standard (n1256):
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
The important thing to remember here is that there is a difference between an object (in C terms, meaning something that takes up memory) and the expression used to refer to that object.
When you declare an array such as
int a[10];
the object designated by the expression a is an array (i.e., a contiguous block of memory large enough to hold 10 int values), and the type of the expression a is "10-element array of int", or int [10]. If the expression a appears in a context other than as the operand of the sizeof or & operators, then its type is implicitly converted to int *, and its value is the address of the first element.
In the case of the sizeof operator, if the operand is an expression of type T [N], then the result is the number of bytes in the array object, not in a pointer to that object: N * sizeof T.
In the case of the & operator, the value is the address of the array, which is the same as the address of the first element of the array, but the type of the expression is different: given the declaration T a[N];, the type of the expression &a is T (*)[N], or pointer to N-element array of T. The value is the same as a or &a[0] (the address of the array is the same as the address of the first element in the array), but the difference in types matters. For example, given the code
int a[10];
int *p = a;
int (*ap)[10] = &a;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
you'll see output on the order of
p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80
IOW, advancing p adds sizeof int (4) to the original value, whereas advancing ap adds 10 * sizeof int (40).
More standard language:
6.5.2.1 Array subscripting
Constraints
1 One of the expressions shall have type ‘‘pointer to object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.
Semantics
2 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).
Thus, when you subscript an array expression, what happens under the hood is that the offset from the address of the first element in the array is computed and the result is dereferenced. The expression
a[i] = 10;
is equivalent to
*((a)+(i)) = 10;
which is equivalent to
*((i)+(a)) = 10;
which is equivalent to
i[a] = 10;
Yes, array subscripting in C is commutative; for the love of God, never do this in production code.
Since array subscripting is defined in terms of pointer operations, you can apply the subscript operator to expressions of pointer type as well as array type:
int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
p[i] = some_initial_value();
Here's a handy table to remember some of these concepts:
Declaration: T a[N];
Expression Type Converts to Value
---------- ---- ------------ -----
a T [N] T * Address of the first element in a;
identical to writing &a[0]
&a T (*)[N] Address of the array; value is the same
as above, but the type is different
sizeof a size_t Number of bytes contained in the array
object (N * sizeof T)
*a T Value at a[0]
a[i] T Value at a[i]
&a[i] T * Address of a[i]
Declaration: T a[N][M];
Expression Type Converts to Value
---------- ---- ------------ -----
a T [N][M] T (*)[M] Address of the first subarray (&a[0])
&a T (*)[N][M] Address of the array (same value as
above, but different type)
sizeof a size_t Number of bytes contained in the
array object (N * M * sizeof T)
*a T [M] T * Value of a[0], which is the address
of the first element of the first subarray
(same as &a[0][0])
a[i] T [M] T * Value of a[i], which is the address
of the first element of the i'th subarray
&a[i] T (*)[M] Address of the i-th subarray; same value as
above, but different type
sizeof a[i] size_t Number of bytes contained in the i'th subarray
object (M * sizeof T)
*a[i] T Value of the first element of the i'th
subarray (a[i][0])
a[i][j] T Value at a[i][j]
&a[i][j] T * Address of a[i][j]
Declaration: T a[N][M][O];
Expression Type Converts to
---------- ---- -----------
a T [N][M][O] T (*)[M][O]
&a T (*)[N][M][O]
*a T [M][O] T (*)[O]
a[i] T [M][O] T (*)[O]
&a[i] T (*)[M][O]
*a[i] T [O] T *
a[i][j] T [O] T *
&a[i][j] T (*)[O]
*a[i][j] T
a[i][j][k] T
From here, the pattern for higher-dimensional arrays should be clear.
So, in summary: arrays are not pointers. In most contexts, array expressions are converted to pointer types.
Arrays are not pointers, though in most expressions an array name evaluates to a pointer to the first element of the array. So it is very, very easy to use an array name as a pointer. You will often see the term 'decay' used to describe this, as in "the array decayed to a pointer".
One exception is as the operand to the sizeof operator, where the result is the size of the array (in bytes, not elements).
A couple additional of issues related to this:
An array parameter to a function is a fiction - the compiler really passes a plain pointer (this doesn't apply to reference-to-array parameters in C++), so you cannot determine the actual size of an array passed to a function - you must pass that information some other way (maybe using an explicit additional parameter, or using a sentinel element - like C strings do)
Also, a common idiom to get the number of elements in an array is to use a macro like:
#define ARRAY_SIZE(arr) ((sizeof(arr))/sizeof(arr[0]))
This has the problem of accepting either an array name, where it will work, or a pointer, where it will give a nonsense result without warning from the compiler. There exist safer versions of the macro (particularly for C++) that will generate a warning or error when it's used with a pointer instead of an array. See the following SO items:
C++ version
a better (though still not perfectly safe) C version
Note: C99 VLAs (variable length arrays) might not follow all of these rules (in particular, they can be passed as parameters with the array size known by the called function). I have little experience with VLAs, and as far as I know they're not widely used. However, I do want to point out that the above discussion might apply differently to VLAs.
sizeof is evaluated at compile-time, and the compiler knows whether the operand is an array or a pointer. For arrays it gives the number of bytes occupied by the array. Your array is a char[] (and sizeof(char) is 1), thus sizeof happens to give you the number of elements. To get the number of elements in the general case, a common idiom is (here for int):
int y[20];
printf("number of elements in y is %lu\n", sizeof(y) / sizeof(int));
For pointers sizeof gives the number of bytes occupied by the raw pointer type.
In
char hello[] = "hello there"
int i;
and
char* hello = "hello there";
int i;
In the first instance (discounting alignment) 12 bytes will be stored for hello with the allocated space initialised to hello there while in the second hello there is stored elsewhere (possibly static space) and hello is initialised to point to the given string.
hello[2] as well as *(hello + 2) will return 'e' in both instances however.
In addition to what the others said, perhaps this article helps: http://en.wikipedia.org/wiki/C_%28programming_language%29#Array-pointer_interchangeability
If 'y' is a constant pointer, why does it have a size of 20, like the sequence of values it points to?
Because z is the address of the variable, and will always return 8 for your machine. You need to use the dereference pointer (&) in order to get the contents of a variable.
EDIT: A good distinction between the two: http://www.cs.cf.ac.uk/Dave/C/node10.html