Spec-Zone.ru › C++

Объявление массива

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

Синтаксис

Объявление массива — это любое простое объявление, у которого декларатор имеет вид

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

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

Объекты типа массив не могут быть изменены в целом: даже если они являются 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/w/cpp/language/array

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API