Array declaration
Объявляет объект типа массив.
СодержаниеСинтаксисОбъявление массива — это любое простое объявление, чей объявитель имеет форму
Объявление вида Массивы могут быть построены из любых фундаментальных типов (кроме
Не существует массивов ссылок или массивов функций. Применение 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;
Говорят, что ПрисваиваниеОбъекты массива не могут быть изменены целиком: несмотря на то, что они являются 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 массивов могут быть сформированы с использованием псевдонима типа для создания временного массива с помощью функционального приведения с инициализацией в фигурных скобках .
Массивные 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++.
Смотрите также
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||