Namespaces
Variants

Array declaration

From ru.cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Объявляет объект типа массив.

Содержание

Синтаксис

Объявление массива — это любое простое объявление, чей объявитель имеет форму

noptr-declarator [expr (необязательно)] attr (необязательно)
noptr-declarator - любой корректный объявитель, но если он начинается с *, & или &&, его необходимо окружить круглыми скобками (в противном случае весь объявитель рассматривается как объявитель указателя или объявитель ссылки).
expr - интегральное константное выражение(до C++14) преобразованное константное выражение типа std::size_t(начиная с C++14), которое вычисляется в значение больше нуля
attr - (начиная с C++11) список атрибутов

Объявление вида T a[N]; объявляет a как массив-объект, состоящий из N непрерывно размещённых объектов типа T. Элементы массива нумеруются 0, …, N - 1, и к ним можно обращаться с помощью оператора индексации [], например a[0], …, a[N - 1].

Массивы могут быть построены из любых фундаментальных типов (кроме void), указателей, указателей на члены, классов, перечислений или из других массивов известного размера (в этом случае массив называется многомерным). Другими словами, только объектные типы, за исключением типов массивов неизвестного размера, могут быть типами элементов массивов. Типы массивов с неполным типом элемента также являются неполными типами.

Спецификатор возможно ограниченный(начиная с C++20) auto может использоваться как тип элемента массива в объявлении указателя или ссылки на массив, который выводит тип элемента из инициализатора или аргумента функции(начиная с C++14), например auto (*p)[42] = &a; допустим, если a является lvalue типа int[42].

(начиная с C++11)

Не существует массивов ссылок или массивов функций.

Применение cv-квалификаторов к типу массива (через typedef или манипуляции с типами шаблонов) применяет квалификаторы к типу элемента, но любой тип массива, чьи элементы имеют cv-квалифицированный тип, считается имеющим ту же cv-квалификацию.

// a and b have the same const-qualified type "array of 5 const char"

typedef const char CC;
CC a[5] = {};

typedef char CA[5];
const CA b = {};

При использовании с new[]-выражением размер массива может быть нулевым; такой массив не содержит элементов:

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

На практике, для упрощения формулировки, указатель на элемент массива иногда называют указателем на массив:

int arr[5] {};
int *p = arr;

Говорят, что p указывает на массив arr, но, строго говоря, он указывает на первый элемент arr. В частности, если p не указывает на первый элемент arr, он может представлять поддиапазон arr.

Присваивание

Объекты массива не могут быть изменены целиком: несмотря на то, что они являются lvalues (например, можно взять адрес массива), они не могут находиться в левой части оператора присваивания:

int a[3] = {1, 2, 3}, b[3] = {4, 5, 6};
int (*p)[3] = &a; // корректно: можно взять адрес массива a
a = b;            // ошибка: a является массивом
struct { int c[3]; } s1, s2 = {3, 4, 5};
s1 = s2; // корректно: неявно определенный оператор присваивания копированием
         // может присваивать члены данных типа массива

Преобразование массива в указатель

Существует неявное преобразование из lvalue и rvalue массива в rvalue указателя: оно создает указатель на первый элемент массива. Это преобразование используется всякий раз, когда массивы появляются в контексте, где ожидаются указатели, а не массивы:

#include <iostream>
#include <iterator>
#include <numeric>
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'  // выводит размер массива
              << sizeof p << '\n'; // выводит размер указателя
    // где допустимы массивы, но не указатели, могут использоваться только массивы
    g(a); // окей: функция принимает массив по ссылке
//  g(p); // ошибка
    for (int n : a)            // окей: массивы можно использовать в циклах range-for
        std::cout << n << ' '; // выводит элементы массива
//  for (int n : p)            // ошибка
//      std::cout << n << ' ';
    std::iota(std::begin(a), std::end(a), 7); // окей: begin и end принимают массивы
//  std::iota(std::begin(p), std::end(p), 7); // ошибка
    // где допустимы указатели, но не массивы, могут использоваться оба:
    f(a); // окей: функция принимает указатель
    f(p); // окей: функция принимает указатель
    std::cout << *a << '\n' // выводит первый элемент
              << *p << '\n' // то же самое
              << *(a + 1) << ' ' << a[1] << '\n'  // выводит второй элемент
              << *(p + 1) << ' ' << p[1] << '\n'; // то же самое
}

Многомерные массивы

Когда тип элемента массива сам является массивом, говорят, что массив многомерный:

// массив из 2 массивов по 3 int каждый
int a[2][3] = {{1, 2, 3},  // может рассматриваться как матрица 2 × 3
               {4, 5, 6}}; // с построчным расположением элементов

Обратите внимание, что при применении decay от массива к указателю многомерный массив преобразуется в указатель на свой первый элемент (например, указатель на свою первую строку или на свою первую плоскость): decay от массива к указателю применяется только один раз.

int a[2];            // массив из 2 int
int* p1 = a;         // a преобразуется в указатель на первый элемент a
int b[2][3];         // массив из 2 массивов по 3 int
// int** p2 = b;     // ошибка: b не преобразуется в int**
int (*p2)[3] = b;    // b преобразуется в указатель на первую 3-элементную строку b
int c[2][3][4];      // массив из 2 массивов по 3 массива по 4 int
// int*** p3 = c;    // ошибка: c не преобразуется в int***
int (*p3)[3][4] = c; // c преобразуется в указатель на первую плоскость 3 × 4 элемента c

Массивы неизвестной границы

Если expr опущен в объявлении массива, объявленный тип является "массив неизвестной границы T", что представляет собой разновидность неполного типа , за исключением случаев использования в объявлении с агрегатной инициализацией :

extern int x[];      // тип x - "массив int неизвестного размера"
int a[] = {1, 2, 3}; // тип a - "массив из 3 int"

Поскольку элементы массива не могут быть массивами неизвестной границы, многомерные массивы не могут иметь неизвестную границу в размерности, отличной от первой:

extern int a[][2]; // корректно: массив неизвестной границы массивов из 2 int
extern int b[2][]; // ошибка: массив имеет неполный тип элемента

Если существует предшествующее объявление сущности в той же области видимости, в которой был указан размер, опущенный размер массива принимается таким же, как в этом более раннем объявлении, и аналогично для определения статического члена данных класса:

extern int x[10];
struct S
{
    static int y[10];
};
int x[];               // OK: размер равен 10
int S::y[];            // OK: размер равен 10
void f()
{
    extern int x[];
    int i = sizeof(x); // ошибка: неполный тип объекта
}

Ссылки и указатели на массивы неизвестной границы могут быть образованы, но не могут (до C++20) и могут (начиная с C++20) быть инициализированы или присвоены из массивов и указателей на массивы известной границы. Отметим, что в языке программирования C указатели на массивы неизвестной границы совместимы с указателями на массивы известной границы и, следовательно, конвертируемы и присваиваемы в обоих направлениях.

extern int a1[];
int (&r1)[] = a1;  // корректно
int (*p1)[] = &a1; // корректно
int (*q)[2] = &a1; // ошибка (но корректно в C)
int a2[] = {1, 2, 3};
int (&r2)[] = a2;  // корректно (начиная с C++20)
int (*p2)[] = &a2; // корректно (начиная с C++20)

Указатели на массивы неизвестной границы не могут участвовать в арифметике указателей и не могут использоваться слева от оператора индексации , но могут быть разыменованы.

Массивы как r-значения

Хотя массивы не могут возвращаться из функций по значению и не могут быть целью большинства выражений приведения, prvalues массивов могут быть сформированы с использованием псевдонима типа для создания временного массива с помощью функционального приведения с инициализацией в фигурных скобках .

Подобно prvalue-выражениям классов, prvalue-выражения массивов преобразуются в xvalues через материализацию временного объекта при вычислении.

(since C++17)

Массивные xvalues могут быть образованы напрямую через обращение к элементу массива класса rvalue или с использованием std::move или другого приведения/вызова функции, возвращающего rvalue-ссылку.

#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';           // размер массива
    f(X().i);                                    // окей: связывается с xvalue
//  f(x.i);                                      // ошибка: нельзя связать с lvalue
    int a[2][3];
    f(std::move(a));                             // окей: связывается с xvalue
    using arr_t = int[2][3];
    f(arr_t{});                                  // окей: связывается с prvalue
    f(identity<int[][3]>{{1, 2, 3}, {4, 5, 6}}); // окей: связывается с prvalue
}

Вывод:

24
24
24
24
24

Отчеты о дефектах

Следующие отчеты об изменениях в поведении, содержащие описания дефектов, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 393 C++98 указатель или ссылка на массив неизвестного
размера не могли быть параметрами функции
разрешено
CWG 619 C++98 при опускании размер массива не мог
быть выведен из предыдущего объявления
вывод разрешён
CWG 2099 C++98 размер статического члена данных типа массив не мог
быть опущен даже при наличии инициализатора
опускание разрешено
CWG 2397 C++11 auto не мог использоваться как тип элемента разрешено

Смотрите также

C documentation для Array declaration