Массивы в C++: C‑массивы и std::array — понятное практическое руководство
Массивы в C++: C‑массивы vs std::array — практическое руководство для начинающих
Запрос, который часто вводят в поиск: «массивы в C++ std::array для начинающих». В этой статье вы разберётесь, чем отличаются C‑массивы и std::array, как правильно передавать их в функции, как использовать со стандартными алгоритмами и где чаще всего случаются ошибки. В конце — мини‑практикум и рекомендации, что выбрать: C‑массив, std::array или std::vector.
1) Что такое C‑массив и с чего начинается боль
#include <iostream>
int main() {
int a[5] = {1, 2, 3}; // недостающие элементы обнулятся: {1,2,3,0,0}
std::cout << a[0] << "\n"; // 1
std::cout << sizeof(a) << "\n"; // 20 (на тип int по 4 байта) — только здесь, в этой области видимости!
}
Особенность C‑массива: при передаче в функцию он «растворяется» до указателя на первый элемент, и информация о размере теряется. Из‑за этого новички часто ошибаются с sizeof и выходом за границы.
Неправильная передача C‑массива в функцию
#include <iostream>
void print_bad(int arr[]) { // arr == int*; размер неизвестен!
// Плохо: sizeof(arr) == sizeof(int*), а не размер массива в байтах
std::cout << "sizeof(arr) = " << sizeof(arr) << "\n";
}
int main() {
int a[5] = {1,2,3,4,5};
print_bad(a);
}
Как исправить? Либо передавать размер отдельно, либо — лучше — передавать ссылку на массив известного размера через шаблон.
Правильно: шаблонная ссылка на массив
#include <iostream>
template <std::size_t N>
void print_array(const int (&arr)[N]) {
for (std::size_t i = 0; i < N; ++i) {
std::cout << arr[i] << (i + 1 == N ? "\n" : " ");
}
}
int main() {
int a[5] = {1,2,3,4,5};
print_array(a); // N=5 выведен из типа
}
Это безопасно и даёт компилятору знать размер на этапе компиляции. Но работать так с каждым C‑массивом неудобно. Поэтому есть std::array.
2) std::array: фиксированный размер, безопасность и удобство
std::array<T, N> — контейнер фиксированной длины из стандартной библиотеки. Он хранит элементы прямо внутри себя (как C‑массив), но при этом:
- знает свой размер:
.size() - работает со стандартными алгоритмами STL
- имеет методы
front,back,fill,data - безопаснее:
at()проверяет границы и бросает исключение
#include <iostream>
#include <array>
int main() {
std::array<int, 5> a = {1,2,3,4,5};
std::cout << a.size() << "\n"; // 5
std::cout << a.front() << "\n"; // 1
std::cout << a.back() << "\n"; // 5
a[0] = 10; // без проверки
// a.at(10) = 42; // std::out_of_range (если раскомментировать)
for (int x : a) std::cout << x << ' ';
}
3) Алгоритмы STL со std::array
Алгоритмы работают «из коробки», поскольку std::array предоставляет итераторы.
#include <iostream>
#include <array>
#include <algorithm> // sort, reverse, find
int main() {
std::array<int, 6> a = {3,1,4,1,5,9};
std::sort(a.begin(), a.end());
std::reverse(a.begin(), a.end());
auto it = std::find(a.begin(), a.end(), 4);
if (it != a.end()) {
std::cout << "found at index: " << std::distance(a.begin(), it) << "\n";
}
}
4) Взаимодействие с C‑API: .data()
Нужно передать «сырой» указатель в функцию C‑библиотеки? У std::array есть .data() — адрес первого элемента.
#include <array>
#include <cstring> // memcpy
int main() {
std::array<char, 8> buf{};
const char* src = "hello";
std::memcpy(buf.data(), src, 6); // включая завершающий \0
}
5) Универсальная передача массивов: std::span (C++20)
std::span<T> — «не владеющий» вид на непрерывную последовательность. Подходит для C‑массивов, std::array и std::vector без копирования и без потери размера.
#include <span>
#include <vector>
#include <array>
#include <iostream>
int sum(std::span<const int> s) {
int acc = 0;
for (int x : s) acc += x;
return acc;
}
int main() {
int c_arr[3] = {1,2,3};
std::array<int,3> a = {4,5,6};
std::vector<int> v = {7,8,9};
std::cout << sum(c_arr) << "\n"; // 6
std::cout << sum(a) << "\n"; // 15
std::cout << sum(v) << "\n"; // 24
}
Если у вас C++17 и ниже — можно принимать пару итераторов или указатель+длину, но span заметно упрощает интерфейс.
6) Частые ошибки с массивами
- Использование
sizeofна параметре функции, где массив уже «схлопнулся» до указателя. Решение:std::array,std::spanили шаблонная ссылка. - Выход за границы (
arr[i]>=arr.size()). Для отладки используйтеat()уstd::array— он бросает исключение. - Ожидание «динамической» длины от
std::array. Его размер фиксирован во время компиляции. Нужна изменяемая длина — используйтеstd::vector. - Забыли инициализировать. Для C‑массивов используйте список инициализации в фигурных скобках;
std::arrayможно сбросить методомfill.
7) Мини‑практикум: частотность и фильтрация
Посчитаем количество чётных элементов и отфильтруем исходную последовательность в новый буфер. Реализуем на std::span и std::array.
#include <array>
#include <span>
#include <iostream>
std::size_t count_even(std::span<const int> s) {
std::size_t cnt = 0;
for (int x : s) if (x % 2 == 0) ++cnt;
return cnt;
}
// Фильтруем чётные в целевой std::array фиксированного размера
// Возвращаем реальное число записанных элементов
std::size_t filter_even(std::span<const int> in, std::span<int> out) {
std::size_t w = 0;
for (int x : in) {
if (x % 2 == 0) {
if (w < out.size()) out[w++] = x; // защита от переполнения
}
}
return w;
}
int main() {
std::array<int, 8> src = {3, 6, 1, 8, 10, 5, 4, 7};
std::array<int, 8> dst{};
std::cout << "even count = " << count_even(src) << "\n";
std::size_t n = filter_even(src, dst);
for (std::size_t i = 0; i < n; ++i) std::cout << dst[i] << ' '; // 6 8 10 4
}
8) Что выбрать: C‑массив, std::array или std::vector?
- C‑массив: минимум накладных расходов, хорош для низкоуровневого кода и взаимодействия с C‑API. Требует дисциплины в передаче размера и проверке границ.
- std::array: фиксированная длина + удобный интерфейс STL. Идеален для небольших статических буферов, константных таблиц, быстрых стековых структур.
- std::vector: когда размер должен меняться во время выполнения. Если сомневаетесь — начинайте с
vector.
9) Советы и лучшие практики
- Для фиксированного размера в современном C++ почти всегда предпочтителен
std::array. - Для универсальных API используйте
std::span<T>(C++20) вместо пары указатель+длина. - При отладке обращений по индексу у
std::arrayиспользуйтеat()— поймаете выход за границы раньше. - Не злоупотребляйте
memcpy; для тривиальных типов ок, но для нетривиальных используйте алгоритмы и копирование элемент‑за‑элементом.
Хотите быстро и системно прокачать основы и практику современного C++ с задачами и обратной связью? Рекомендую посмотреть программу и начать обучение по курсу: Стартовать в C++ с нуля до гуру — пошаговый курс с практикой.
Итог
Если вам нужен фиксированный по размеру массив — выбирайте std::array. Он безопаснее и удобнее C‑массива, дружит с алгоритмами и облегчает написание корректного кода. Для изменяемых по размеру коллекций используйте std::vector. А чтобы делать универсальные функции без копирования — применяйте std::span (C++20).
-
Создано 13.03.2026 17:01:37
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.