Arrays are derived data types, representing an ordered collection of values ("elements") of another type. Most arrays in C have a fixed number of elements of any one type, and its representation stores the elements contiguously in memory without gaps or padding. C allows multidimensional arrays whose elements are other arrays, and also arrays of pointers.
C supports dynamically allocated arrays whose size is determined at run time. C99 and later supports variable length arrays or VLAs.
Why do we need arrays?
Arrays provide a way to organize objects into an aggregate with its own significance. For example, C strings are arrays of characters (char
s), and a string such as "Hello, World!" has meaning as an aggregate that is not inherent in the characters individually. Similarly, arrays are commonly used to represent mathematical vectors and matrices, as well as lists of many kinds. Moreover, without some way to group the elements, one would need to address each individually, such as via separate variables. Not only is that unwieldy, it does not easily accommodate collections of different lengths.
Arrays are implicitly converted to pointers in most contexts.
Except when appearing as the operand of the sizeof
operator, the _Alignof
operator (C2011), or the unary &
(address-of) operator, or as a string literal used to initialize an(other) array, an array is implicitly converted into ("decays to") a pointer to its first element. This implicit conversion is tightly coupled to the definition of the array subscripting operator ([]
): the expression arr[idx]
is defined as be equivalent to *(arr + idx)
. Furthermore, since pointer arithmetic is commutative, *(arr + idx)
is also equivalent to *(idx + arr)
, which in turn is equivalent toidx[arr]
. All of those expressions are valid and evaluate to the same value, provided that either idx
or arr
is a pointer (or an array, which decays to a pointer), the other is an integer, and the integer is a valid index into the array to which the pointer points.
As a special case, observe that &(arr[0])
is equivalent to &*(arr + 0)
, which simplifies to arr
. All of those expressions are interchangeable wherever the last decays to a pointer. This simply expresses again that an array decays to a pointer to its first element.
In contrast, if the address-of operator is applied to an array of type T[N]
(i.e. &arr
) then the result has type T (*)[N]
and points to the whole array. This is distinct from a pointer to the first array element at least with respect to pointer arithmetic, which is defined in terms of the size of the pointed-to type.
Function parameters are not arrays.
void foo(int a[], int n);
void foo(int *a, int n);
Although the first declaration of foo
uses array-like syntax for parameter a
, such syntax is used to declare a function parameter declares that parameter as a pointer to the array's element type. Thus, the second signature for foo()
is semantically identical to the first. This corresponds to the decay of array values to pointers where they appear as arguments to a function call, such that if a variable and a function parameter are declared with the same array type then that variable's value is suitable for use in a function call as the argument associated with the parameter.