Array declaration
Объявляет объект типа массив.
Содержание |
Синтаксис
Объявление массива — это любое простое объявление, в котором декларатор имеет вид
noptr-declarator
[
expr
(необязательно)
]
attr
(необязательно)
|
|||||||||
| noptr-declarator | - |
любой допустимый
declarator
, но если он начинается с
*
,
&
или
&&
, он должен быть окружён круглыми скобками (иначе весь декларатор трактуется как
pointer declarator
или
reference declarator
).
|
| expr | - | целочисленное constant expression (до C++14) преобразованное constant expression типа std::size_t (начиная с C++14) , которое вычисляется в значение больше нуля |
| attr | - | (начиная с C++11) список attributes |
Объявление вида
T a
[
N
]
;
объявляет
a
как массивный
объект
, состоящий из
N
непрерывно расположенных объектов типа
T
. Элементы массива нумеруются
0
, …,
N
-
1
и могут быть доступны с помощью
оператора индексирования []
, как в
a
[
0
]
, …,
a
[
N
-
1
]
.
Массивы могут быть созданы из любого фундаментального типа (кроме void ), указателей , указателей на члены , классов , перечислений или из других массивов известной длины (в этом случае массив называется многомерным). Другими словами, только объектные типы, за исключением типов массивов неизвестной длины, могут быть типами элементов массивов. Типы массивов с неполным типом элемента также являются неполными типами.
|
Спецификатор
возможно
ограниченный
(since C++20)
|
(since C++11) |
Не существует массивов ссылок или массивов функций.
Применение cv-квалификаторов к типу массива (через typedef или манипуляции с шаблонными типами) применяет квалификаторы к типу элементов, но любой тип массива, элементы которого имеют cv-квалифицированный тип, считается имеющим ту же самую cv-квалификацию.
// a и b имеют одинаковый квалифицированный const тип "массив из 5 const char" typedef const char CC; CC a[5] = {}; typedef char CA[5]; const CA b = {};
При использовании с new[]-выражением размер массива может быть нулевым; такой массив не имеет элементов:
int* p = new int[0]; // доступ к p[0] или *p неопределен delete[] p; // очистка все еще требуется
Присваивание
Объекты массива не могут быть изменены целиком: несмотря на то, что они являются 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
|