Массивы С++ и работа с ними
Сегодня в нашей статье мы поговорим о том, что такое массив и для чего он используется. Разберемся с многомерными массивами и узнаем, как происходит передача в функцию. Кроме того, здесь мы поделимся с вами некоторыми секретами, которые смогут значительно облегчить ваш путь через тернии к звездам. Желаем приятного чтения!
Массив и его важнейшие понятия
Массив — это одноименной участок памяти, который содержит в себе определенную последовательность одинакового типа элементов. Таким образом, это просто набор однотипных объектов.
Любой массив можно охарактеризовать следующими приведенными ниже определениями:
- Элемент массива – это определенное значение, принадлежащее ячейке памяти, которая находится в пределах этого массива.
- Адрес массива – это адрес первого элемента.
- Имя массива – это идентификатор, который используют для обращения к элементам, принадлежащим массиву.
- Размер массива – кол-во всех объектов данного массива. Обратите внимание, что ваш массив должен обладать константным размером. Получается, что невозможно, запросить у пользователя какой-либо размер, а затем задать его.
- Размер элемента – кол-во байт, которое занимает один элемент массива.
Особенности массива
- Каждый массив должен иметь какое-либо название
- В теории количество элементов в его составе начинается от одного и заканчивается бесконечностью. На практике все решает память вашего компьютера, в котором вы и будете хранить данный массив.
- Как мы уже говорили, все элементы должны быть однотипными. К слову, вы не сможете одновременно хранить вместе переменные double и int.
Использование массивов в C
Если вникнуть и разобраться, работа с массивами покажется вам довольно легкой. Небольшой лайфхак: в силу того, что массивы содержат в своем составе значения одного типа, для обработки данных рекомендуется использовать цикл for.
Инициализация массивов
Если вы хотите успешно использовать массивы в любых своих программах, то для начала его нужно инициализировать. Для этого обычно применяется следующая схема:
<тип> <имя массива> [ <кол-во элементов > ];
Давайте рассмотрим простой пример:
double name[300];
Выходит, что с помощью этого кода у нас получился массив типа double, носящий название name и имеющий возможность хранить в себе о 300 однотипных элементов.
Обращение к элементам
Хотим сразу вынести такую вещь, как нумерация ваших массивов. Обратите внимание, что в языке C она начинается с нуля. Получается, второй элемент обладает индексом 1, а десятый — индексом 9. Начинающие программисты иногда забывают об этом, что приводит к различным казусам.
Прежде всего вам нужно научиться работать с присвоением нужных значений элементам вашего массива. Это не слишком сложно: сначала вы расписывается название массива и номер того элемента, который вам необходим. А затем, после знака =, вписываете значение, которое хотите присвоить.
Давайте рассмотрим такой случай:
name[2] = 8 * 5; //name[2] равен 40
Если же вам нужно получить доступ к определенному объекту своего массива, вам нужно воспользоваться следующей схемой:
<любой код> <название массива> [ <No. элемента> ] <любой код>
Как это будет выглядеть в нашем случае:
cout << name[7] << «, » << name[8] << endl;
Вот так вот мы вывели значения восьмого и девятого объектов.
Многомерные массивы
В отношении массивов нередко применяется такое понятие как ранг или, как оно называется по-другому, кол-во измерений. Все массивы, о которых мы разговаривали выше имеют одно измерение (мы смело можем говорить, что их ранг = 1). Одноранговые массивы часто представляются в виде горизонтального ряда.
Наверное, вы уже догадались, что в противоположность таким массивам существуют и другие — многомерными. Если отталкиваться от названия, мы можем сказать, что такие массивы обладают количеством измерений (или по-другому рангом), превышающим 1.
Если массив имеет два измерения (ну, или ранга), его именуют двухмерным.
Сейчас мы покажем пример создания одно- и двумерного массивов с одинаковыми элементами в своем составе:
int [ ] nums1 = new int[] { 0, 8, 9, 11, 17, 19 };
int [,] nums2 = { { 0, 8, 9 }, { 11, 17, 19 } };
Переполнение массива
Когда вы работаете с таким типом памяти, вам постоянно нужно отслеживать его данные. Очень важно просматривать счетчик: во-первых, он не должен превышать размер вашего массива, во-вторых, он не должен быть меньше нуля. Наши советы по максимально эффективному предотвращению этих двух условий:
- Для индексирования лучше всего подойдет size_t. Он сможет предотвратить появление отрицательных чисел на счетчике, а кроме того, он идеально подходит для любого размера массива.
- Последний элемент всегда обладает индексом.
- Как мы уже говорили, не забывайте, что массив начинается не с единицы, а с нуля. Можете держать в голове, что индекс последнего объекта на 1 меньше размера вашего массива.
Передача массива в функцию
Массив C обладает только указателем на начало и, к сожалению, не хранит свой размер, что доставляет много неудобств, например, возникновение сложности во время передачи в функцию. Да, без проблем можно посмотреть размер в процессе компилирования, но такая возможность целиком и полностью отсутствует в процессе самого выполнения. Однако передать размер вполне себе реально, мы подобрали для вас несколько вариантов того, как это можно сделать:
Первый способ — передача указателя на начало, размер массива.
void printArrayV1(int* values, size_t size)
{for (size_t i = 0; i < size; ++i)
{std::cout << values[i] << std::endl;}}
Второй способ — передача ссылки на массив, чей размер нам известен.
constexpr size_t AGES_COUNT = 3;
void printArrayV2(int (&values)[AGES_COUNT])
{for (size_t i = 0; i < std::size(values); ++i)
{std::cout << values[i] << std::endl;}}
Есть еще третий способ — использовать gsl::span, однако он считается наиболее сложным и продвинутым.
int main ( )
{// Индексы элементов: 4, 5, 6
int ages[] = {5, 6, 7};
printArrayV1(ages, std::size(ages));
printArrayV2(ages);
Динамически изменяемый массив
Простые массивы свой размер не изменяют. Да, можно задать переменную, которая бы отражала число занятых и используемых ячеек. Однако вы все равно не сможете внести больше объектов, чем задавалось разработчиком во время компиляции в месте массива.
Добавление элементов в конец массива
Если вы хотите добавить какие-либо элементы в конец, у вас есть два способа, как это можно сделать:
- Первый — push_back. Сначала он получает значение, а затем выносит его в самый конец.
- Второй — emplace_back. Этот способ немного сложнее и требует больше знаний: вам нужно снабдить его параметрами, которые необходимы желаемому элементу. А затем emplace_back перенесет и вынесет их в конце вашего массива.
Без разницы, какой способ использовать. Зачастую пользователи отдают предпочтение первому — push_back.
Также есть крайне полезный способ под названием pop_back, который вы можете применять с целью удаления каких-либо объектов. Вот как это работает:
int main()
{ = { 8, 5, 6 }.
std::vector<int> ages;
ages.push_back(8);
ages.push_back(5);
ages.push_back(6);
// убираем последний элемент
ages.pop_back ( );
Перемещение объектов в памяти при изменении содержимого
Динамический массив, о котором мы говорили выше, хранит свои объекты в так называемой “динамической памяти” (часто по-другому ее называют “куча”, от английского слова heap). Если происходит добавление слишком большого количества объектов, динамический массив вынужден перераспределять свою память. Так происходит, потому что выделенной до этого памяти уже недостаточно для того, чтобы хранить все элементы.
В таких ситуациях обычно происходит так, что vector начинает посылать запрос на новую область для памяти, объем которой в среднем вдвое больше предыдущего. Затем он переносит все ранее созданные элементы, в конец выводит новые, а заключительным этапом становится освобождение ранее занятой области для памяти.
Надеемся, что данная статья была вам полезна. Свое мнение и отзывы вы можете оставить в комментариях. Успехов в программировании!