c/language/array

Array is a type consisting of a contiguously allocated nonempty sequence of objects with a particular element type. The number of those objects (the array size) never changes during the array lifetime.

Syntax
In the of an array declaration, the type-specifier sequence designates the element type (which must be a complete object type), and the declarator has the form:

@1,2@ General array declarator syntax @3@ Declarator for VLA of unspecified size (can appear in function prototype scope only) where

Explanation
There are several variations of array types: arrays of known constant size, variable-length arrays, and arrays of unknown size.

Arrays of constant known size
If in an array declarator is an  with a value greater than zero, then the declarator declares an array of constant known size:

Arrays of constant known size can use to provide their initial values:

{{rev|since=c99| In function parameter lists, additional syntax elements are allowed within the array declarators: the keyword {{ttb|static}} and {{spar|qualifiers}}, which may appear in any order before the size expression (they may also appear even when the size expression is omitted).

In each {{rlp|operator_other#Function call|function call}} to a function where an array parameter uses the keyword {{ttb|static}} between {{ttb|[}} and {{ttb|]}}, the value of the actual parameter must be a valid pointer to the first element of an array with at least as many elements as specified by : {{source|1= void fadd(double a[static 10], const double b[static 10]) {   for (int i = 0; i < 10; i++) { if (a[i] < 0.0) return; a[i] += b[i]; } } // a call to fadd may perform compile-time bounds checking // and also permits optimizations such as prefetching 10 doubles int main(void) {   double a[10] = {0}, b[20] = {0}; fadd(a, b); // OK   double x[5] = {0}; fadd(x, b); // undefined behavior: array argument is too small } }}

If {{spar|qualifiers}} are present, they qualify the pointer type to which the array parameter type is transformed: {{source|1= int f(const int a[20]) { // in this function, a has type const int* (pointer to const int) } int g(const int a[const 20]) { // in this function, a has type const int* const (const pointer to const int) } }} This is commonly used with the {{rlpt|restrict}} type qualifier: {{source|1= void fadd(double a[static restrict 10],         const double b[static restrict 10]) {   for (int i = 0; i < 10; i++) { // loop can be unrolled and reordered if (a[i] < 0.0) break; a[i] += b[i]; } } }}

Variable-length arrays
If is not an, the declarator is for an array of variable size.

Each time the flow of control passes over the declaration, is evaluated (and it must always evaluate to a value greater than zero), and the array is allocated (correspondingly,  of a VLA ends when the declaration goes out of scope). The size of each VLA instance does not change during its lifetime, but on another pass over the same code, it may be allocated with a different size.

If the size is, the declaration is for a VLA of unspecified size. Such declaration may only appear in a function prototype scope, and declares an array of a complete type. In fact, all VLA declarators in function prototype scope are treated as if were replaced by.

Variable-length arrays and the types derived from them (pointers to them, etc) are commonly known as "variably-modified types" (VM). Objects of any variably-modified type may only be declared at block scope or function prototype scope.

VLA must have automatic or allocated storage duration. Pointers to VLA, but not VLA themselves may also have static storage duration. No VM type may have linkage. {{source|1= void fvla(int m, int C[m][m]) // OK: block scope/auto duration pointer to VLA {   typedef int VLA[m][m]; // OK: block scope VLA int D[m];             // OK: block scope/auto duration VLA // static int E[m]; // Error: static duration VLA // extern int F[m]; // Error: VLA with linkage int (*s)[m];    // OK: block scope/auto duration VM    s = malloc(m * sizeof(int)); // OK: s points to VLA in allocated storage // extern int (*r)[m]; // Error: VM with linkage static int (*q)[m] = &B; // OK: block scope/static duration VM} } }}

Variably-modified types cannot be members of structs or unions.

}}

Arrays of unknown size
If in an array declarator is omitted, it declares an array of unknown size. Except in function parameter lists (where such arrays are transformed to pointers) and when an  is available, such type is an :

Assignment
Objects of array type are not, and although their address may be taken, they cannot appear on the left hand side of an assignment operator. However, structs with array members are modifiable lvalues and can be assigned:

Array to pointer conversion
Any of array type, when used in any context other than
 * as the operand of the
 * as the operand of
 * as the string literal used for

undergoes an to the pointer to its first element. The result is not an lvalue.

If the array was declared, the behavior of the program that attempts such conversion is undefined.

When an array type is used in a function parameter list, it is transformed to the corresponding pointer type: and  declare the same function. Since the function's actual parameter type is pointer type, a function call with an array argument performs array-to-pointer conversion; the size of the argument array is not available to the called function and must be passed explicitly:

Multidimensional arrays
When the element type of an array is another array, it is said that the array is multidimensional:

Note that when array-to-pointer conversion is applied, a multidimensional array is converted to a pointer to its first element, e.g., pointer to the first row: