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"

Because array element cannot have incomplete type, multidimensional arrays cannot have unknown bound in a dimension other than the first:

extern int a[][2]; // OK: array of unknown bound of arrays of 2 int
extern int b[2][]; // Error: array has incomplete element type

References and pointers to arrays of unknown bound can be formed, but cannot be initialized or assigned from arrays and pointers to arrays of known bound. Note that in the C programming language, pointers to arrays of unknown bound are compatible with pointers to arrays of known bound and are thus convertible and assignable in both directions.

extern int a1[];
int (&r1)[] = a1; // OK
int (*p1)[] = &a1; // OK 
int (*q)[2] = &a1; // Error (but OK in C)
int a2[] = {1,2,3};
int (&r2)[] = a2; // Error
int (*p2)[] = &a2; // Error (but OK in C)

Pointers to arrays of unknown bound cannot participate in pointer arithmetic and cannot be used on the left of the subscript operator, but can be dereferenced. Pointers and references to arrays of unknown bound cannot be used in function parameters (until C++14).

[edit] Array rvalues

Although arrays cannot be returned from functions by value and cannot be targets of most cast expressions, array prvalues may be formed by using a type alias to construct an array temporary using brace-initialized functional cast. Array xvalues may be formed by accessing an array member of a class rvalue or by using std::move or another cast or function call that returns an rvalue reference.

#include <iostream>
#include <type_traits>
#include <utility>
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 xvalue
//  f(x.i);                                      // error: cannot bind to lvalue
    int a[2][3];
    f(std::move(a));                             // okay: binds to xvalue
    using arr_t = int[2][3];
    f(arr_t{});                                  // okay: binds to prvalue
    f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // okay: binds to prvalue