Перейти к содержимому
Теория · Строки и массивы · урок 3 из 6

C-массивы и std::array

Представьте ряд одинаковых шкафчиков в раздевалке. У ряда одно общее название, а у каждого шкафчика свой номер, и вещь находят по этому номеру. Шкафчики стоят вплотную друг к другу, и их число не меняется: новый шкафчик в ряд не втиснуть.

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

C++
#include <iostream>

int main() {
    int scores[5] = {90, 75, 60, 85, 70};
    int sum = 0;
    for (int s : scores) {
        sum = sum + s;
    }
    std::cout << scores[0] << ' ' << scores[4] << '\n';  // 90 70
    std::cout << sum << '\n';  // 380
}

Запись int scores[5] объявляет массив из пяти int, которые лежат в памяти подряд. Элементы нумеруются с нуля, поэтому последний — scores[4]. Цикл по диапазону проходит по всем пяти элементам.

Такой массив встроен в сам язык: C++ получил его от языка C, поэтому его называют C-массивом.

Размер — часть типа

Число в квадратных скобках входит в тип: scores имеет тип int[5], а int[3] — уже другой тип. Поэтому размер должен быть известен при компиляции: это литерал или constexpr-переменная. sizeof(scores) даёт размер всего массива в байтах, а если разделить его на размер одного элемента, получится число элементов:

C++
int scores[5] = {90, 75, 60, 85, 70};
std::cout << sizeof(scores) / sizeof(scores[0]) << '\n';  // 5

Свои границы C-массив не проверяет. Номер за пределами массива, например scores[5], даёт неопределённое поведение: шкафчика с таким номером нет, и что получит программа, стандарт не определяет.

Массив в параметре

Скопировать C-массив в параметр функции нельзя. Вместо этого функция получает указатель на первый элемент. Это называют преобразованием массива в указатель. Параметр int a[5] означает то же, что int* a, а число в скобках компилятор игнорирует.

Внутри функции sizeof(a) поэтому даёт размер указателя, а не массива, и длину по нему не узнать. Её передают в функцию отдельным параметром, как n в задачах на указатели.

std::array

Массив типа std::array<int, 5> из заголовка <array> хранит те же пять int подряд, но копируется и передаётся в функцию целиком, так же как переменная типа int. В указатель он не превращается, а size() возвращает число элементов. Передают его по const-ссылке, как и другие объекты, которые дорого копировать.

C++
#include <array>
#include <iostream>

int total(const std::array<int, 5>& values) {
    int sum = 0;
    for (int v : values) {
        sum = sum + v;
    }
    return sum;
}

int main() {
    std::array<int, 5> scores = {90, 75, 60, 85, 70};
    std::cout << scores.size() << ' ' << total(scores) << '\n';  // 5 380
}

В угловых скобках стоят тип элементов и их число. Размер и здесь входит в тип, поэтому total принимает только массив ровно из пяти int.

Частая ошибка

Число элементов считают внутри функции:

C++
int count(int a[5]) {
    return sizeof(a) / sizeof(a[0]);  // ✗
}
TEXT
warning: sizeof on array function parameter will return size of 'int *' instead of 'int[5]' [-Wsizeof-array-argument]

Это предупреждение, а не ошибка: функция компилируется, но a в ней — указатель. sizeof(a) даёт размер указателя, и результат не зависит от длины массива. Передайте длину вторым параметром или возьмите std::array.

Главное

  • C-массив хранит элементы одного типа подряд, и его размер входит в тип.
  • Номер за пределами массива даёт неопределённое поведение.
  • В параметре функции C-массив превращается в указатель, поэтому длину передают отдельно.
  • std::array копируется целиком, а size() возвращает число его элементов.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом