C-массивы и std::array
Представьте ряд одинаковых шкафчиков в раздевалке. У ряда одно общее название, а у каждого шкафчика свой номер, и вещь находят по этому номеру. Шкафчики стоят вплотную друг к другу, и их число не меняется: новый шкафчик в ряд не втиснуть.
Пять оценок можно хранить в пяти переменных, но сложить их в цикле не получится: у каждой своё имя. Массив хранит несколько значений одного типа под одним именем, как ряд шкафчиков, а к каждому значению обращаются по номеру:
#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) даёт размер всего массива в байтах, а если разделить его на размер одного элемента, получится число элементов:
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-ссылке, как и другие объекты, которые дорого копировать.
#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.
Частая ошибка
Число элементов считают внутри функции:
int count(int a[5]) {
return sizeof(a) / sizeof(a[0]); // ✗
}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 и подпишись на канал — откроются все уроки.