Урок №75. Фиксированные массивы

  Юрий  | 

  Обновл. 2 мая 2019  | 

 18024

 ǀ   11 

Этот урок является продолжением первой части о массивах в C++.

Инициализация фиксированных массивов

Элементы массива обрабатываются так же, как и обычные переменные, поэтому они не инициализируются при создании. Одним из способов инициализации массива является присваивание значений каждому элементу поочерёдно:

Однако это не совсем удобно, особенно когда массив большой.

К счастью, C++ поддерживает более удобный способ инициализации массивов с помощью списка инициализаторов. Следующий пример эквивалентен примеру выше:

Если в этом списке инициализаторов больше, чем может содержать массив, то компилятор выдаст ошибку.

Однако, если в списке инициализаторов меньше, чем может содержать массив, то остальные элементы будут инициализированы значением 0. Например:

Результат выполнения программы выше:

5
7
9
0
0

Следовательно, чтобы инициализировать все элементы массива значением 0, нужно:

В C++11 вместо этого может использоваться синтаксис uniform-инициализации:

Длина массива


Если вы инициализируете фиксированный массив с помощью списка инициализаторов, то компилятор может определить длину массива вместо вас, и вам уже не потребуется её объявлять.

Следующие две строчки выполняют одно и то же:

Это не только сэкономит время, но также вам не придётся обновлять длину массива, если вы захотите добавить или удалить элементы позже.

Массивы и перечисления

Одна из основных проблем с документацией в массивах состоит в том, что целочисленные индексы не предоставляют никакой информации программисту об их значении. Рассмотрим класс из 5 учеников:

Кто представлен элементом testScores[3]? Непонятно!

Это можно решить, используя перечисление, в котором перечислители сопоставляются каждому из возможных индексов массива:

Теперь-то понятно, что представляет собой каждый из элементов массива. Обратите внимание, добавлен дополнительный перечислитель с именем MAX_STUDENTS. Он используется во время объявления массива для гарантирования того, что массив имеет корректную длину (она должна быть на единицу больше самого большого индекса). Это полезно как в целях документации, так и в том, что массив будет автоматически изменён, если добавить ещё один перечислитель:

Обратите внимание, этот трюк работает только в том случае, если вы не изменяете значения перечислителей вручную!

Массивы и классы enum


Классы enum не имеют неявного преобразования в целочисленный тип, поэтому, если вы попробуете сделать следующее:

То получите ошибку от компилятора. Это можно решить, используя оператор static_cast для конвертации перечислителя в целое число:

Однако, это также не очень удобно, поэтому лучше использовать стандартное перечисление внутри пространства имён:

Передача массивов в функции

Хотя передача массива в функцию на первый взгляд выглядит так же, как передача обычной переменной, но «под капотом» C++ обрабатывает массивы несколько иначе.

Когда обычная переменная передаётся по значению, то C++ копирует значение аргумента в параметр функции. Поскольку параметр является копией, то изменение значения параметра не изменяет значение исходного аргумента.

Однако, поскольку копирование больших массивов — дело трудоёмкое, то C++ не копирует массив при его передаче в функцию. Вместо этого передаётся фактический массив. И здесь мы получаем побочный эффект, позволяющий функциям напрямую изменять значение элементов массива!

Следующий пример хорошо иллюстрирует эту концепцию:

Результат выполнения программы выше:

before passValue: 1
after passValue: 1
before passArray: 1 4 6 8 10
after passArray: 10 8 6 4 1

В примере выше значение переменной value не изменяется в функции main(), так как параметр value в функции passValue() был лишь копией фактической переменной value. Однако, поскольку массив в параметре функции passArray() является фактическим массивом, то passArray() напрямую изменяет значения элементов!

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

Оператор sizeof и массивы


Оператор sizeof можно использовать и с массивами: он возвращает общий размер массива (длина массива умножена на размер одного элемента) в байтах. Обратите внимание, из-за того, как C++ передаёт массивы в функции, эта операция не будет корректно выполнена с массивами, переданными в функции! Например:

Результат выполнения программы выше:

32
4

По этой причине будьте осторожны с использованием оператора sizeof() с массивами!

Определение длины фиксированного массива

Трюк: Чтобы определить длину фиксированного массива, поделите размер всего массива на размер одного элемента массива:

Результат выполнения программы выше:

The array has 8 elements

Как это работает? Во-первых, размер всего массива равен длине массива, умноженной на размер одного элемента. Формула: размер массива = длина массива * размер одного элемента.

Используя алгебру, мы можем изменить это уравнение: длина массива = размер массива / размер одного элемента. sizeof(array) — это размер массива, а sizeof(array [0]) — это размер одного элемента массива. Соответственно, длина массива = sizeof(array) / sizeof(array[0]). Обычно используется нулевой элемент в качестве элемента массива в уравнении, так как только он является единственным элементом, который гарантированно существует в массиве, независимо от его длины.

Это работает только если массив фиксированной длины, и вы выполняете эту операцию в той же функции, в которой объявлен массив (мы поговорим больше о том, почему это ограничение существует, в следующих уроках этой главы).

Термины «размер массива» и «длина массива» чаще всего используются для обозначения длины массива (размер массива в большинстве случаев не очень полезен, разве что в трюке, который мы показали выше). Поэтому в следующих уроках мы будем использовать термин «длина» для обозначения общего количества элементов в массиве, и «размер», когда речь будет идти о байтах.

Индексирование массива вне диапазона


Помните, что массив длиной N содержит элементы от 0 до N-1. Итак, что произойдёт, если мы попытаемся получить доступ к индексу массиву за пределами этого диапазона? Рассмотрим следующую программу:

Здесь наш массив имеет длину 5, но мы пытаемся записать значение в 6-ой элемент (индекс 5).

C++ не выполняет никаких проверок корректности вашего индекса. Таким образом, в примере выше значение 14 будет помещено в ячейку памяти, где 6-ой элемент существовал бы, если бы был. Но, как вы уже догадались, это будет иметь свои последствия. Например, произойдёт перезапись значения другой переменной или вообще сбой программы.

Хотя это происходит реже, но C++ также позволяет использовать отрицательный индекс, что тоже приведёт к нежелательным результатам.

Правило: При использовании массивов убедитесь, что ваши индексы корректны и соответствуют диапазону вашего массива.

Тест

Задание №1

Объявите массив для хранения температуры (дробное число) каждого дня в году (всего 365 дней). Инициализируйте массив значением 0.0 для каждого дня.

Ответ №1

Примечание: Если размер не является ограничением, то вместо float лучше использовать double.

Задание №2

Создайте перечисление со следующими перечислителями: chicken, lion, giraffe, elephant, duck и snake. Поместите перечисление в пространство имён. Объявите массив, где элементами будут эти перечислители и, используя список инициализаторов, инициализируйте каждый элемент соответствующем количеством лапок определённого животного. В функции main() выведите количество лапок у слона, используя перечислитель.

Ответ №2

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (115 оценок, среднее: 4,96 из 5)
Загрузка...

Комментариев: 11

  1. Аватар Алексей:

    После всего — второе задание оказалось простым в исполнении, только текстом не приукрасил.

  2. Аватар Алексей:

    Немного не понял первое — почему double temp[365] = {0.0}.
    Я использовал double temp[365] = { }.

    В том и ином случаи "0".

  3. Аватар Анна:

    Не подскажите, зачем нужно писать
    = {0.0}, если все элементы по умолчанию, без присваивания значений равны 0.
    нельзя ли просто написать
    duoble god[365]; например?
    или это будет неправильно?

  4. Аватар Oleksiy:

    "Трюк: Чтобы определить длину фиксированного массива — разделите размер всего массива на размер одного элемента массива:"

    В русском языке принято употреблять слово "размер" массива, когда речь идет о количестве элементов в нем? Разве "длина" в этом случае не будет более уместным термином? Размер больше ассоциируется с количеством байт.

  5. Аватар Алексей:

    Вместо "трюка" — уравнения с sizeof, на мой взгляд, гораздо удобнее использовать более компактную встроенную функцию:

    Она возвращает количество элементов в контейнере (массивы, vector, string)
    Должна подключаться отдельным заголовком

    но в Visual Studio 2017 работает уже при

    1. Юрий Юрий:

      Да, можно использовать уже встроенную функцию. Встроенная функция даже удобнее, нежели трюк, указанный в статье. Но для общего развития знать не помешает.

    2. Аватар ц3к32к:

      Нет, чувак, тогда выведется предупреждение: <: несоответствие типов со знаком и без знака

  6. Аватар Антон:

    "Тест 1. Инициализируйте массив значением 0.0 для каждого дня."
    "double temperature[365] = { 0.0 };"
    Так ведь инициализируется только temperature[0], значением 0.0. Остальные тоже будут 0, но не потому что мы так захотели. Нужно ведь через
    for (int count = 0; count < 365; ++count)
    temperature[count] = 0.0;
    ну либо double temperature[365] = { 0.0, 0.0, …362раза…, 0.0 };
    или я ошибаюсь?

    1. Юрий Юрий:

      Зачем использовать цикл или 365 раз писать 0.0, если массив инициализируется просто одним лишь { 0.0 } или {}. Есть действия, которые установлены уже по умолчанию, просто так прописывать то, что установлено по умолчанию — зачем? Вы просто напишите лишний код. В этом задании никакой программист писать 365 раз нули не будет, цикл здесь также не уместен. Другое дело уже, если нужно заполнить массив ненулевыми значениями.

      1. Аватар Антон:

        Так я думал задание и состоит в том, чтобы каждому значению массива присвоить число, просто для примера взят 0 и так уж совпало, что это "значение по умолчанию" для элементов массива. Меня смутила фраза в задании "для каждого дня".

        1. Юрий Юрий:

          В задании говорится именно о 0. Если бы было другое значение, то тогда бы уже использовался цикл.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *

telegram канал
НОВОСТИ RAVESLI