11 Declarators [dcl.decl]

11.3 Meaning of declarators [dcl.meaning]

11.3.4 Arrays [dcl.array]

In a declaration T D where D has the form
D1 [ constant-expression ] attribute-specifier-seq
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 auto type-specifier, the program is ill-formed.
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.
The constant expression specifies the bound of (number of elements in) the array.
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.
An object of array type contains a contiguously allocated non-empty set of N subobjects of type 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
:
typedef int A[5], AA[2][3];
typedef const A CA;             // type is “array of 5 const inttypedef const AA CAA;           // type is “array of 2 array of 3 const int
end example
]
[Note
:
An “array of N cv-qualifier-seq T” has cv-qualified type; see [basic.type.qualifier].
end note
]
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
:
float fa[17], *afp[17];
declares an array of float numbers and an array of pointers to float numbers.
end example
]
[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 .
Any of the expressions x3d, x3d[i], x3d[i][j], x3d[i][j][k] can reasonably appear in an expression.
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).
end example
]
[Note
:
The first subscript in the declaration helps determine the amount of storage consumed by an array but plays no other part in subscript calculations.
end note
]
[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
}
end example
]
[Note
:
Conversions affecting expressions of array type are described in [conv.array].
Objects of array types cannot be modified, see [basic.lval].
end note
]
[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.
end note
]