Пространства имён
Варианты
Действия

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

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Обьявления
Объявления
ссылка
указатель
массив
Block declarations
простое объявление
объявление структурных привязок (C++17)
объявление псевдонимов(C++11)
объявление псевдонимов пространств имён
using-declaration
директива using
объявление static_assert (C++11)
определение asm
объявление непрозрачного enum(C++11)
Другие объявления
определение пространств имён
объявление функции
объявление шаблона класса
объявление шаблона функции
явное инстанцирование шаблона(C++11)
явная специализация шаблона
спецификация связывания
объявление атрибута (C++11)
пустое объявление
 

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

Содержание

[править] Синтакс

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

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

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

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

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

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

Применение 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; // очистка всё равно требуется

[править] Присваивание

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

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

[править] Приведение массива к указателю

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

#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); // ok: функция принимает массив по ссылке
//  g(p); // ошибка
 
    for(int n: a)              // ok: массивы могут быть использованы в основанных
                               // на диапазоне циклах for
        std::cout << n << ' '; // выводит элементы массива
//  for(int n: p)              // ошибка
//      std::cout << n << ' ';
 
    std::iota(std::begin(a), std::end(a), 7); // ok: begin и end принимают массивы
//  std::iota(std::begin(p), std::end(p), 7); // ошибка
 
    // где указатели допустимы, а массивы нет, могут быть использованы как одни,
    // так и другие:
    f(a); // ok: функция принимает указатель
    f(p); // ok: функция принимает указатель
 
    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}}; // с построчной компоновкой

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

int a[2];            // массив из 2-ух int
int* p1 = 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

[править] Массивы с неизвестными границами

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

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

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

extern int a[][2]; // ok: массив с неизвестными границами, содержащий массивы из 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;  // ok
int (*p1)[] = &a1; // ok
int (*q)[2] = &a1; // ошибка (но ok в C)
 
int a2[] = {1, 2, 3};
int (&r2)[] = a2;  // ok (начиная с C++20)
int (*p2)[] = &a2; // ok (начиная с C++20)

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

[править] Массив значений rvalue

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

Как и значения prvalue класса, значения prvalue массива преобразуются в значения xvalue с помощью временной материализации при вычислении.

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

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

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

Вывод:

24
24
24
24
24

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

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

[править] Смотри также

Документация C по Объявление массива