Array declaration

< cpp‎ | language

Declares an object of array type.


[edit] Syntax

An array declaration is any simple declaration whose declarator has the form

noptr-declarator [ constexpr(optional) ] attr(optional) (1)
noptr-declarator - any valid declarator, but if it begins with *, &, or &&, it has to be surrounded by parentheses.
attr(C++11) - optional list of attributes
constexpr - an integral constant expression (until C++14)a converted constant expression of type std::size_t (since C++14), which evaluates to a value greater than zero

A declaration of the form T a[N];, declares a as an array object that consists of N contiguously allocated objects of type T. The elements of an array are numbered 0...N-1, and may be accessed with the subscript operator [], as in a[0], …, a[N-1].

Arrays can be constructed from any fundamental type (except void), pointers, pointers to members, classes, enumerations, or from other arrays (in which case the array is said to be multi-dimensional). There are no arrays of references, arrays of functions, or arrays of abstract class types.

Applying cv-qualifiers to an array type (through typedef or template type manipulation) applies the qualifiers to the element type, but any array type whose elements are of cv-qualified type is considered to have the same cv-qualification.

// arr1 and arr2 have the same const-qualified type "array of 5 const char"
typedef const char CC;
CC arr1[5] = {}; 
typedef char CA[5];
const CA arr2 = {};

When used with new[]-expression, the size of an array may be zero; such an array has no elements:

int* p = new int[0]; // accessing p[0] or *p is undefined
delete[] p; // cleanup still required

[edit] Assignment

Objects of array type cannot be modified as a whole: even though they are lvalues (e.g. an address of array can be taken), they cannot appear on the left hand side of an assignment operator:

int a[3] = {1, 2, 3}, b[3] = {4, 5, 6};
int (*p)[3] = &a; // okay: address of a can be taken
a = b;            // error: a is an array
struct { int c[3]; } s1, s2 = {3, 4, 5};
s1 = s2; // okay: implicity-defined copy assignment operator
         // can assign data members of array type

[edit] Array-to-pointer decay

There is an implicit conversion from lvalues and rvalues of array type to rvalues of pointer type: it constructs a pointer to the first element of an array. This conversion is used whenever arrays appear in context where arrays are not expected, but pointers are:

#include <iostream>
#include <numeric>
#include <iterator>
void g(int (&a)[3])
    std::cout << a[0] << '\n';
void f(int* p)
    std::cout << *p << '\n';
int main()
    int a[3] = {1, 2, 3};
    int* p = a;
    std::cout << sizeof a << '\n'  // prints size of array
              << sizeof p << '\n'; // prints size of a pointer
    // where arrays are acceptable, but pointers aren't, only arrays may be used
    g(a); // okay: function takes an array by reference
//  g(p); // error
    for(int n: a)              // okay: arrays can be used in range-for loops
        std::cout << n << ' '; // prints elements of the array
//  for(int n: p)              // error
//      std::cout << n << ' ';
    std::iota(std::begin(a), std::end(a), 7); // okay: begin and end take arrays
//  std::iota(std::begin(p), std::end(p), 7); // error
    // where pointers are acceptable, but arrays aren't, both may be used:
    f(a); // okay: function takes a pointer
    f(p); // okay: function takes a pointer
    std::cout << *a << '\n' // prints the first element
              << *p << '\n' // same
              << *(a + 1) << ' ' << a[1] << '\n'  // prints the second element
              << *(p + 1) << ' ' << p[1] << '\n'; //same

[edit] Multidimensional arrays

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

// array of 2 arrays of 3 ints each
int a[2][3] = {{1, 2, 3},  // can be viewed as a 2 × 3 matrix
               {4, 5, 6}}; // with row-major layout

Note that when array-to-pointer implicit conversion is applied, a multidimensional array is converted to a pointer to its first element (e.g., a pointer to the first row or to its first plane): array-to-pointer conversion is applied only once.

int a[2];                // array of 2 ints
int* p1 = a;             // pointer to the first element of a
int b[2][3];             // 2 × 3 matrix
// int** p2 = b;         // error: b does not decay to int**
int (*p2)[3] = b;        // pointer to the first 3-element row of b
int c[2][3][4];          // 2 × 3 × 4 three-dimensional array
int (*p3)[3][4] = c;     // pointer to the first 3 × 4-element plane of c

[edit] Arrays of unknown bound

If constexpr is omitted in the declaration of an array, the type declared is "array of unknown bound of T", which is a kind of incomplete type, except when used in a declaration with an aggregate initializer:

extern int x[];      // the type of x is "array of unknown bound of int"
int a[] = {1, 2, 3}; // the type of a is "array of 3 int"

[edit] Array rvalues

An array rvalue expression may be formed by accessing an array member of a class rvalue or by using an identity template to construct an array temporary directly:

#include <iostream>
#include <type_traits>
void f(int (&&x)[2][3])
    std::cout << sizeof x << '\n';
struct X
    int i[2][3];
} x;
template<typename T> using identity = T;
int main()
    std::cout << sizeof X().i << '\n';           // size of the array
    f(X().i);                                    // okay: binds to rvalue
//  f(x.i);                                      // error: cannot bind to lvalue
    f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // okay: binds to rvalue