In a declaration
T
D
where
D
has the form
*type-specifier*,
the program is ill-formed.

D1 [and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type; if the type of the identifier of D contains the autoconstant-expression]attribute-specifier-seq

T
is called the array
*element type*;
this type shall not be a reference type, cv void,
a function type or an abstract class type.

If the
*constant-expression*
is present, it shall be a converted constant
expression of type std::size_t and
its value shall be greater than zero.

If the value of the constant expression is
N,
the array has
N
elements numbered
0
to
N-1,
and the type of the identifier of
D
is “derived-declarator-type-list array of
N
T”.

Except as noted below, if
the constant expression is omitted, the type of the identifier of
D
is “derived-declarator-type-list array of unknown bound of
T”,
an incomplete object type.

The type “derived-declarator-type-list array of
N
T”
is a different type from the type
“derived-declarator-type-list array of unknown bound of
T”,
see [basic.types].

Any type of the form
“*cv-qualifier-seq* array of
N
T”
is adjusted to
“array of
N
*cv-qualifier-seq*
T”,
and similarly for
“array of unknown bound of
T”.

The optional *attribute-specifier-seq* appertains to the array.

[ Example

: *end example*

]typedef int A[5], AA[2][3]; typedef const A CA; // type is “array of 5 const int” typedef const AA CAA; // type is “array of 2 array of 3 const int”—

An array can be constructed from one of the fundamental types
(except
void),
from a pointer,
from a pointer to member, from a class,
from an enumeration type,
or from another array.

When several “array of” specifications are adjacent, a
multidimensional array
type is created;
only the first of
the constant expressions that specify the bounds
of the arrays may be omitted.

In addition to declarations in which an incomplete object type is allowed,
an array bound may be omitted in some cases in the declaration of a function
parameter ([dcl.fct]).

An array bound may also be omitted
when the declarator is followed by an
*initializer* or
when a declarator for a static data member is followed by a
*brace-or-equal-initializer* ([class.mem]).

In both cases the bound is calculated from the number
of initial elements (say,
N)
supplied ([dcl.init.aggr]),
and the type of the identifier of
D
is “array of
N
T”.

Furthermore, if there is a preceding declaration of the entity in the same
scope in which the bound was specified, an omitted array bound is taken to
be the same as in that earlier declaration, and similarly for the definition
of a static data member of a class.

[ Example

: *end example*

]int x3d[3][5][7];

declares an array of three elements,
each of which is an array of five elements,
each of which is an array of seven integers.

The overall array can be viewed as a
three-dimensional array of integers,
with rank .

The expression
x3d[i]
is equivalent to
*(x3d + i);
in that expression,
x3d
is subject to the array-to-pointer conversion
and is first converted to
a pointer to a 2-dimensional
array with rank
that points to the first element of x3d.

Then i is added,
which on typical implementations involves multiplying
i by the
length of the object to which the pointer points,
which is sizeof(int).

The result of the addition and indirection is
an lvalue denoting
the ith array element of
x3d
(an array of five arrays of seven integers).

If there is another subscript,
the same argument applies again, so
x3d[i][j] is
an lvalue denoting
the jth array element of
the ith array element of
x3d
(an array of seven integers), and
x3d[i][j][k] is
an lvalue denoting
the kth array element of
the jth array element of
the ith array element of
x3d
(an integer).

— [ Example

: *end example*

]extern int x[10]; struct S { static int y[10]; }; int x[]; // OK: bound is 10 int S::y[]; // OK: bound is 10 void f() { extern int x[]; int i = sizeof(x); // error: incomplete object type }—

[ Note

: *end note*

]Conversions affecting expressions of array type are described in [conv.array].

Objects of array types cannot be modified, see [basic.lval].

— [ Note

: *end note*

]Except where it has been declared for a class ([over.sub]),
the subscript operator
[]
is interpreted
in such a way that
E1[E2]
is identical to
*((E1)+(E2)) ([expr.sub]).

Because of the conversion rules
that apply to
+,
if
E1
is an array and
E2
an integer,
then
E1[E2]
refers to the
E2-th
member of
E1.

Therefore,
despite its asymmetric
appearance, subscripting is a commutative operation.

—