Массив (array) — последовательность элементов одного типа, расположенных непрерывно в памяти, с доступом по индексу через одно имя. Размер обычного массива — константа, известная на этапе компиляции.

Зачем нужно

Сто чисел не хранят в ста переменных: их долго придумывать, а главное — переменные нельзя перебирать в цикле. Элементы массива перебираются по номеру:

int array[100];
for (int i = 0; i < 100; ++i) {
  std::cin >> array[i];
}

Как работает

Объявление и доступ

int array[10];  // 10 int подряд
 
array[0];  // первый
array[9];  // последний: нумерация с нуля

Элементы идут подряд без разрывов: &a[i] - &a[j] == i - j.

Размер в скобках — положительная константа времени компиляции. int array[n] с n из std::cin стандарт C++ запрещает; для такого случая есть Динамический массив.

Размер

sizeof(array);                // 40: суммарный размер в байтах
sizeof(array) / sizeof(int);  // 10: число элементов

См. sizeof.

Инициализация

int a[10];          // не инициализирован
int z[10]{};        // нули
int b[5]{1, 2, 3};  // 1 2 3 0 0: недостающее — нули
int c[]{1, 2, 3};   // размер выводится: int[3]

Ошибка в лонгриде L04

В лонгриде нули задаются как int a[10]();, а выведение размера — как int b[1]{1, 2, 3}. Оба варианта не компилируются (GCC 16.2: «array of functions» и «too many initializers»). Здесь приведены рабочие формы, см. L04 - Массивы.

Массив и указатель

В большинстве ситуаций массив неявно превращается в указатель на нулевой элемент:

int array[10];
int* p = array;
 
std::cout << array + 5;  // адрес array[5]
p[2];                    // то же, что *(p + 2)
2[p];                    // да, тоже *(2 + p)

Индексирование — сдвиг указателя и разыменование, см. Арифметика указателей.

Но массив — не указатель:

sizeof(array);  // 40
sizeof(p);      // 8
 
&array;  // int(*)[10]: указатель на массив из 10 int
&p;      // int**

Правила

  1. Нельзя создать массив ссылок или функций; массив указателей и массив указателей на функции — можно: int* c[30], int (*d[40])(int).
  2. Нельзя определить массив неизвестного размера (int a[]; — storage size of 'a' isn't known), но можно объявить: extern int c[];. См. Объявление и определение.
  3. a == b сравнивает адреса нулевых элементов, не значения: для разных массивов всегда false.
  4. Массивы нельзя присваивать и копировать: a = b; и int b[5] = a; — ошибки. Исключение — строка при инициализации массива char.
  5. Если массив — поле структуры, присваивание структур копирует и массив.

Многомерные массивы

int b[5][20];    // двумерный
b[3][11];
 
int a[2][3]{1, 2, 3, 4};             // по строкам: 1 2 3 / 4 0 0
int c[3][2]{{1, 2}, {3, 4}, {5, 6}};
int d[][2]{1, 2, 3, 4};              // выводится только первая размерность

Многомерный массив — массив массивов. Его первый элемент — массив меньшей размерности, к указателю на который он и приводится:

int a[1][2][3];
a[0];  // int[2][3]
 
int b[2][3];
b[1][1] <=> *(b[1] + 1) <=> *(*(b + 1) + 1);

b + 1 сдвигается на целую строку — sizeof(int[3]) байт.

Тонкости

Не из лекции

  • Сравнение массивов через == в C++20 устарело, в C++26 — ошибка компиляции (GCC 16.2).
  • Обращение за пределы массива, включая array[10] при десяти элементах, — Неопределённое поведение. Компилятор границы не проверяет.
  • Когда массив передают в функцию, туда уходит указатель — и sizeof(array) / sizeof(int) внутри функции даёт уже не число элементов.

Частые ошибки

  • Задавать размер переменной: int a[n].
  • Обращаться к a[N] в массиве из N элементов.
  • Считать число элементов через sizeof у указателя.
  • Сравнивать массивы через ==, ожидая сравнения содержимого.
  • Присваивать массив массиву.

Углубление — L05

Массив в функцию передаётся указателем на нулевой элемент — и функция не знает, сколько элементов можно смотреть. Два способа это сообщить:

void PrintArray(const int* data, int size);         // начало + количество
void PrintRange(const int* begin, const int* end);  // начало + конец
 
int values[] = {3, 1, 4};
PrintArray(values, 3);
PrintRange(values, values + 3);

Во втором способе end указывает сразу за последний элемент: получается полуинтервал [begin, end). Указатель values + 3 допустим как граница, но разыменовывать его нельзя. При begin == end диапазон пустой. Так же устроены стандартные алгоритмы: std::sort(values, values + 3) из <algorithm>.

const int* в параметрах — функции только читают элементы. Подробнее о способах передачи — Передача параметров.