9.1 – Массивы (часть 1)

Добавлено 4 июня 2021 в 18:13
Глава 9 – Массивы, строки, указатели и ссылки  (содержание)

Примечание. Эта глава немного сложнее предыдущих. Если вы будете немного озадачены, держитесь. Лучшее еще впереди!

В уроке «8.4 – Структуры» мы узнали, что с помощью структуры можно объединить множество различных типов данных в один идентификатор. Структура отлично подходит для случая, когда мы хотим смоделировать один объект, который имеет много разных свойств. Однако она не очень хорошо подходит для случая, когда мы хотим отслеживать множество связанных экземпляров чего-либо.

К счастью, структуры – не единственный агрегированный тип данных в C++. Массив – это агрегированный тип данных, который позволяет нам получить доступ ко многим переменным одного и того же типа через один идентификатор.

Рассмотрим случай, когда вы хотите записать результаты тестов для 30 учеников в классе. Без массивов вам пришлось бы выделить 30 почти идентичных переменных!

// выделяем 30 целочисленных переменных (все имена разные)
int testScoreStudent1{};
int testScoreStudent2{};
int testScoreStudent3{};
// ...
int testScoreStudent30{};

Массивы дают нам гораздо более простой способ сделать это. Следующее определение массива, по сути, эквивалентно:

// размещаем 30 целочисленных переменных в массиве фиксированного размера
int testScore[30]{}; 

В объявлении переменной массива мы используем квадратные скобки ([]), чтобы сообщить компилятору как о том, что это переменная массива (а не обычная переменная), так и о том, сколько переменных в нем следует разместить (так называемая длина массива).

В приведенном выше примере мы объявляем фиксированный массив с именем testScore длиной 30. Фиксированный массив (также называемый массивом фиксированной длины или массивом фиксированного размера) – это массив, длина которого известна во время компиляции. При создании экземпляра testScore будет выделено место под 30 целочисленных значений типа int.

Элементы массива и индексирование

Каждая из переменных в массиве называется элементом. У элементов нет собственных уникальных имен. Вместо этого для доступа к отдельным элементам массива мы используем имя массива вместе с оператором индекса ([]) и параметром, называемым индексом, который сообщает компилятору, какой элемент нам нужен. Этот процесс называется индексированием массива.

В приведенном выше примере первым элементом нашего массива является testScore[0]. Второй – testScore[1]. Десятый – testScore[9]. Последний элемент в нашем массиве testScoretestScore[29]. Это здорово, потому что нам больше не нужно отслеживать кучу разных (но связанных) имен – мы можем просто изменять индекс для доступа к различным элементам.

Важно: в отличие от повседневной жизни, где мы обычно начинаем отсчет с 1, в C++ массивы всегда начинают отсчет с 0!

Для массива длиной N элементы массива пронумерованы от 0 до N-1. Это называется диапазоном массива.

Пример программы с массивом

Ниже приведен пример программы, которая объединяет определение и индексацию массива:

#include <iostream>
 
int main()
{
    int prime[5]{}; // храним первые 5 простых чисел
    prime[0] = 2;   // первый элемент имеет индекс 0
    prime[1] = 3;
    prime[2] = 5;
    prime[3] = 7;
    prime[4] = 11; // последний элемент имеет индекс 4 (длина массива-1)
 
    std::cout << "The lowest prime number is: " << prime[0] << '\n';
    std::cout << "The sum of the first 5 primes is: " << prime[0] + prime[1] + prime[2] + prime[3] + prime[4] << '\n';
 
    return 0;
}

Эта программа напечатает:

The lowest prime number is: 2
The sum of the first 5 primes is: 28

Типы данных массива

Массивы можно создавать из данных любого типа. Рассмотрим следующий пример, где мы объявляем массив значений типа double:

#include <iostream>
 
int main()
{
    double batteryLifeInHours[3]{}; // размещаем 3 значения типа double
    batteryLifeInHours[0] = 2.0;
    batteryLifeInHours[1] = 3.0;
    batteryLifeInHours[2] = 4.3;
 
    std::cout << "The average battery life is " 
              << (batteryLifeInHours[0] + batteryLifeInHours[1] + batteryLifeInHours[2]) / 3.0
              << "hour(s)\n";
 
    return 0;
}

Эта программа дает следующий результат:

The average is 3.1

Массивы также можно создавать из структур. Рассмотрим следующий пример:

struct Rectangle
{
    int length{};
    int width{};
};
Rectangle rects[5]{}; // объявляем массив из 5 Rectangle (прямоугольников)

Чтобы получить доступ к члену структуры элемента массива, сначала выберите, какой элемент массива вам нужен, а затем используйте оператор выбора члена, чтобы выбрать член структуры, который вам нужен:

rects[0].length = 24;

Массивы можно даже создавать из массивов – тема, которую мы рассмотрим в одном из следующих уроков.

Индексы массива

В C++ индексы массива всегда должны быть целочисленного типа. Сюда входят char, short, int, long, long long и т.д. ... и, как ни странно, bool (где false дает индекс 0, а true дает индекс 1). Индекс массива может быть литералом, переменной (константной или неконстантной) или выражением, которое вычисляется как целочисленный тип.

Вот несколько примеров:

// объявляем массив длиной 5
int array[5]{}; 
 
// использование в качестве индекса литерала (константы):
array[1] = 7; // ok
 
// использование в качестве индекса перечисления (константы):
enum Animals
{
    animal_cat = 2
};
array[animal_cat] = 4; // ok
 
// использование в качестве индекса переменной (неконстанты):
short index = 3;
array[index] = 7; // ok
 
// // использование в качестве индекса выражения,
// которое вычисляется как целочисленное значение:
array[1+2] = 7; // ok

Объявление фиксированных массивов

При объявлении фиксированного массива длина массива (значение в квадратных скобках) должна быть константой времени компиляции. Это связано с тем, что длина фиксированного массива должна быть известна во время компиляции. Вот несколько разных способов объявления фиксированных массивов:

// использование литеральной константы
int numberOfLessonsPerDay[7]{}; // Ok
 
// использование символьной константы constexpr
constexpr int daysPerWeek{ 7 };
int numberOfLessonsPerDay[daysPerWeek]{}; // Ok
 
// использование перечислителя
enum Weekday
{
    monday,
    tuesday,
    wednesday,
    thursday,
    friday,
    saturday,
    sunday,
    
    maxWeekday
};
int numberOfLessonsPerDay[maxWeekday]{}; // Ok
 
// использование макроса
#define DAYS_PER_WEEK 7
// Работает, но не делайте так (используйте лучше символьную константу constexpr)
int numberOfLessonsPerDay[DAYS_PER_WEEK]{};

Обратите внимание, что неконстантные переменные или константы времени выполнения использовать нельзя:

// использование неконстантной переменной
int daysPerWeek{};
std::cin >> daysPerWeek;
// не подходит - daysPerWeek не является константой времени компиляции!
int numberOfLessonsPerDay[daysPerWeek]{};
 
// использование константной переменной времени выполнения
int temp{ 5 };
// значение daysPerWeek неизвестно до времени выполнения потому,
// что это константа времени выполнения, а не константа времени компиляции!
const int daysPerWeek{ temp }; 
int numberOfLessonsPerDay[daysPerWeek]{}; // не подходит

Обратите внимание, что в последних двух случаях должна возникнуть ошибка, потому что длина не является константой времени компиляции. Некоторые компиляторы могут разрешать такие типы массивов (из-за совместимости с C99), но в C++ они недопустимы и не должны использоваться в программах на C++. Если ваш компилятор позволяет использовать такие массивы, вы, вероятно, забыли отключить расширения компилятора (урок «0.10 – Настройка компилятора: расширения компилятора»).

Замечание о динамических массивах

Поскольку фиксированные массивы занимают память, выделенную во время компиляции, это вводит два ограничения:

  • фиксированные массивы не могут иметь длину, основанную ни на входных данных пользователя, ни на каком-либо другом значении, вычисленном во время выполнения;
  • фиксированные массивы имеют фиксированную длину, которую нельзя изменить.

Во многих случаях эти ограничения проблематичны. К счастью, C++ поддерживает второй тип массивов, известный как динамический массив. Длину динамического массива можно установить во время выполнения, и ее можно изменить. Однако создание динамических массивов немного сложнее, поэтому мы рассмотрим их позже в этой главе.

Резюме

Фиксированные массивы предоставляют простой способ разместить и использовать несколько переменных одного типа, если длина массива известна во время компиляции.

В следующем уроке мы рассмотрим другие темы, связанные с фиксированными массивами.

Теги

C++ / CppLearnCppДля начинающихМассивОбучениеПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.