Contents
1
Syntax
2
Explanation
2.1
Arrays of constant known size
2.2
Variable-length arrays
2.3
Arrays of unknown size
2.4
Qualifiers
2.5
Assignment
2.6
Array to pointer conversion
2.7
Multidimensional arrays
3
Notes
4
References
5
See also
[
edit
]
Syntax
In the
declaration grammar
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:
|
[
static
(optional)
qualifiers
(optional)
expression
(optional)
]
attr-spec-seq
(optional)
|
[
qualifiers
(optional)
static
(optional)
expression
(optional)
]
attr-spec-seq
(optional)
1,2)
General array declarator syntax
3)
Declarator for VLA of unspecified size (can appear in function prototype scope only)
where
|
any expression other than
comma operator
, designates the number of elements in the array
|
qualifiers
|
any combination of
const
,
restrict
, or
volatile
qualifiers, only allowed in function parameter lists; this qualifies the pointer type to which this array parameter is transformed
|
attr-spec-seq
|
(C23)
optional list of
attributes
, applied to the declared array
float fa[11], *afp[17]; // fa is an array of 11 floats
// afp is an array of 17 pointers to floats
[
edit
]
Explanation
There are several variations of array types: arrays of known constant size, variable-length arrays, and arrays of unknown size.
[
edit
]
Arrays of constant known size
If
expression
in an array declarator is an
integer constant expression
with a value greater than zero
and the element type is a type with a known constant size (that is, elements are not VLA)
(since C99)
, then the declarator declares an array of constant known size:
int n[10]; // integer constants are constant expressions
char o[sizeof(double)]; // sizeof is a constant expression
enum { MAX_SZ=100 };
int n[MAX_SZ]; // enum constants are constant expressions
Arrays of constant known size can use
array initializers
to provide their initial values:
int a[5] = {1,2,3}; // declares int[5] initialized to 1,2,3,0,0
char str[] = "abc"; // declares char[4] initialized to 'a','b','c','\0'
In function parameter lists, additional syntax elements are allowed within the array declarators: the keyword
static
and
qualifiers
, which may appear in any order before the size expression (they may also appear even when the size expression is omitted).
In each
function call
to a function where an array parameter uses the keyword
static
between
[
and
]
, 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
expression
:
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 qualifiers are present, they qualify the pointer type to which the array parameter type is transformed:
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 restrict type qualifier:
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 expression is not an integer constant expression, the declarator is for an array of variable size.
Each time the flow of control passes over the declaration, expression is evaluated (and it must always evaluate to a value greater than zero), and the array is allocated (correspondingly, lifetime 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.
#include <stdio.h>
int main(void)
int n = 1;
label:;
int a[n]; // re-allocated 10 times, each with a different size
printf("The array has %zu elements\n", sizeof a / sizeof *a);
if (n++ < 10)
goto label; // leaving the scope of a VLA ends its lifetime
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 expression were replaced by * .
void foo(size_t x, int a[*]);
void foo(size_t x, int a[x])
printf("%zu\n", sizeof a); // same as sizeof(int*)
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.
extern int n;
int A[n]; // Error: file scope VLA
extern int (*p2)[n]; // Error: file scope VM
int B[100]; // OK: file-scope array of constant known size
void fvla(int m, int C[m][m]); // OK: prototype-scope VLA
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.
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.
struct tag
int z[n]; // Error: VLA struct member
int (*y)[n]; // Error: VM struct member
|
(since C99)
|