Объявление массива
Объявляет объект типа массива.
Синтаксис
Объявлением массива является любое простое объявление, чей декларатор имеет вид
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++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
Присваивание
Объекты типа массива нельзя изменять целиком: хотя они являются lvalue (например, можно получить адрес массива), они не могут находиться в левой части оператора присваивания:
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: implicitly-defined copy assignment operator
// can assign data members of array type
Преобразование массива в указатель
Существует неявное преобразование 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' // 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
}
Многомерные массивы
Если типом элемента массива является другой массив, такой массив называется многомерным:
// array of 2 arrays of 3 int each
int a[2][3] = {{1, 2, 3}, // can be viewed as a 2 × 3 matrix
{4, 5, 6}}; // with row-major layout
Обратите внимание: при преобразовании массива в указатель многомерный массив преобразуется в указатель на его первый элемент (например, указатель на первую строку или первую плоскость); преобразование массива в указатель применяется только один раз.
int a[2]; // array of 2 int int* p1 = a; // a decays to a pointer to the first element of a int b[2][3]; // array of 2 arrays of 3 int // int** p2 = b; // error: b does not decay to int** int (*p2)[3] = b; // b decays to a pointer to the first 3-element row of b int c[2][3][4]; // array of 2 arrays of 3 arrays of 4 int // int*** p3 = c; // error: c does not decay to int*** int (*p3)[3][4] = c; // c decays to a pointer to the first 3 × 4-element plane of c
Массивы с неизвестной границей
Если expr опущено в объявлении массива, объявленный тип является «массивом T с неизвестной границей» — разновидностью неполного типа, за исключением случаев, когда объявление используется с инициализатором агрегата:
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"
Поскольку элементы массива не могут быть массивами с неизвестной границей, многомерные массивы не могут иметь неизвестную границу ни в одном измерении, кроме первого:
extern int a[][2]; // okay: array of unknown bound of arrays of 2 int extern int b[2][]; // error: array has incomplete element type
Если в той же области видимости имеется предшествующее объявление сущности, в котором граница была указана, то опущенная граница массива считается такой же, как в этом предыдущем объявлении. Аналогичное правило действует для определения статического члена данных класса:
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
}
Можно создавать ссылки и указатели на массивы с неизвестной границей, но их нельзя(до C++20)и можно(начиная с C++20) инициализировать или присваивать им массивы и указатели на массивы с известной границей. Обратите внимание, что в языке программирования C указатели на массивы с неизвестной границей совместимы с указателями на массивы с известной границей, поэтому преобразование и присваивание допустимы в обоих направлениях.
extern int a1[];
int (&r1)[] = a1; // okay
int (*p1)[] = &a1; // okay
int (*q)[2] = &a1; // error (but okay in C)
int a2[] = {1, 2, 3};
int (&r2)[] = a2; // okay (since C++20)
int (*p2)[] = &a2; // okay (since C++20)
Указатели на массивы с неизвестной границей не могут участвовать в арифметике указателей и не могут находиться слева от оператора индексирования, но могут разыменовываться.
Массивы-rvalue
Хотя массивы нельзя возвращать из функций по значению и нельзя использовать в качестве операндов большинства выражений приведения типов, prvalue массива можно сформировать с помощью псевдонима типа, чтобы создать временный массив посредством функционального приведения с инициализацией фигурными скобками.
| Подобно prvalue классов, prvalue массивов при вычислении преобразуются в xvalue посредством материализации временного объекта. | (начиная с C++17) |
xvalue массива можно сформировать непосредственно, обратившись к члену-массиву 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'; // 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
}
Вывод:
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 о объявлении массива |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/array