Урок №94. Введение в std::array

  Юрий  | 

  |

  Обновл. 13 Сен 2021  | 

 102891

 ǀ   30 

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

Поэтому в Стандартную библиотеку C++ добавили функционал, который упрощает процесс управления массивами: std::array и std::vector. На этом уроке мы рассмотрим std::array, а на следующем — std::vector.

Введение в std::array

Представленный в C++11, std::array — это фиксированный массив, который не распадается в указатель при передаче в функцию. std::array определяется в заголовочном файле array, внутри пространства имен std. Объявление переменной std::array следующее:

Подобно обычным фиксированным массивам, длина std::array должна быть установлена во время компиляции. std::array можно инициализировать с использованием списка инициализаторов или uniform-инициализации:

В отличие от стандартных фиксированных массивов, в std::array вы не можете пропустить (не указывать) длину массива:

Также можно присваивать значения массиву с помощью списка инициализаторов:

Доступ к значениям массива через оператор индекса осуществляется как обычно:

Так же, как и в стандартных фиксированных массивах, оператор индекса не выполняет никаких проверок на диапазон. Если указан недопустимый индекс, то произойдут плохие вещи.

std::array поддерживает вторую форму доступа к элементам массива — функция at(), которая осуществляет проверку диапазона:

В примере, приведенном выше, вызов myarray.at(1) проверяет, есть ли элемент массива под номером 1, и, поскольку он есть, возвращается ссылка на этот элемент. Затем мы присваиваем ему значение 7. Однако, вызов myarray.at(8) не срабатывает, так как элемента под номером 8 в массиве нет. Вместо возвращения ссылки, функция at() выдает ошибку, которая завершает работу программы (на самом деле выбрасывается исключение типа std::out_of_range. Об исключениях мы поговорим на соответствующих уроках). Поскольку проверка диапазона выполняется, то функция at() работает медленнее (но безопаснее), чем оператор [].

std::array автоматически делает все очистки после себя, когда выходит из области видимости, поэтому нет необходимости прописывать это вручную.

Размер и сортировка


С помощью функции size() можно узнать длину массива:

Результат:

length: 4

Поскольку std::array не распадается в указатель при передаче в функцию, то функция size() будет работать, даже если её вызвать из другой функции:

Результат тот же:

length: 4

Обратите внимание, Стандартная библиотека C++ использует термин «размер» для обозначения длины массива — не путайте это с результатами выполнения оператора sizeof с обычным фиксированным массивом, когда возвращается фактический размер массива в памяти (размер_элемента * длина_массива).

Также обратите внимание на то, что мы передаем std::array по (константной) ссылке. Это делается по соображениям производительности для того, чтобы компилятор не выполнял копирование массива при передаче в функцию.

Правило: Всегда передавайте std::array в функции по обычной или по константной ссылке.

Поскольку длина массива всегда известна, то циклы foreach также можно использовать с std::array:

Вы можете отсортировать std::array, используя функцию std::sort(), которая находится в заголовочном файле algorithm:

Результат:

1 2 4 7 8

Функция сортировки использует итераторы, которые мы еще не рассматривали. О них мы поговорим несколько позже.

Заключение

std::array — это отличная замена стандартных фиксированных массивов. Массивы, созданные с помощью std::array, более эффективны, так как используют меньше памяти. Единственными недостатками std::array по сравнению со стандартными фиксированными массивами являются немного неудобный синтаксис и то, что нужно явно указывать длину массива (компилятор не будет вычислять её за нас). Но это сравнительно незначительные нюансы. Рекомендуется использовать std::array вместо стандартных фиксированных массивов в любых нетривиальных задачах.


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

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

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

  1. Гриша:

    std::array автоматически делает все очистки после себя, когда выходит из области видимости, поэтому нет необходимости прописывать это вручную.

    Он использует операторы new и delete? То есть он не в стеке, и следовательно не может вызвать его переполнение?

  2. Антон:

    А поясните пожалуйста, зачем здесь "const"? Вроде читал про это, но не могу найти где..

    в

    1. alex:

      Это скорее всего для того чтобы в функции нельзя было 'случайно изменить массив'

      1. Антон:

        Это вроде я понимаю. Мне не понятно зачем конкретн ов этом коде нужен const, ведь в нем нет ничего, что могло бы изменять массив. Может это связано как-то с тем, что массивы std::array способны целиком передаваться в функцию, не преобразуясь в указатель, как обычные массивы? Может это вспомогательный синтаксис для "&" ? Чтобы тело массива не начало передаваться, а только адрес..? Просто из ранних уроков я заметил что const часто появляется там где есть & …возможно это как то связано ,но не разобрано или я пропустил

        1. Данил:

          Это для того, чтобы было понятно, что функция не меняет массив

        2. Fray:

          Если говорить коротко, то слово const используется как на всякий пожарный для защиты от дальнейших изменений значений в массиве, даже если кажется что все под контролем. Но это лишь малый пример. В "реальности" программы могут быть гораздо большими и уследить за всеми переменными не всегда удается.

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

    2. Terrumor:

      Также обратите внимание на то, что мы передаем std::array по (константной) ссылке. Это делается по соображениям производительности для того, чтобы компилятор не выполнял копирование массива при передаче в функцию.

      1. Антон:

        Ох сколько времени прошло… я уже Питон почти выучил 😀 Некогда изучать с++, а я так люблю его, он мне как первая девушка

    3. Александр:

      const используется вместе с ссылкой для того что бы можно было передавать в функцию r-value. Так как ссылка это неявно разыменованый константный указатель, то присвоить ей мы можем только l-value. Но когда мы делаем ссылку константной она получает способность продолжать время жизни r-value. Прочитать об этом можно здесь https://ravesli.com/urok-89-ssylki-i-const/

  3. Михаил:

    Здравствуйте!

    Извините за частный случай: пишу программу для Wemos D1 mini Pro (головное устройство), для удобства работы с EEPROM, в работающую структуру, объявленную глобально, кроме прочего, был добавлен еще и двумерный массив в виде std:array для хранения адресов и статусов ведомых устройств:

    Все компилируется без предупреждений, но на строке ввода значения в массив, программа вылетает с "exception 29" — несанкционированная попытка записи в запрещенную для записи область памяти.
    ВОПРОС: как правильно вводить данные в двумерный массив помещенный в структуру из других участков программы?

    С уважением.

    1. Фото аватара Дмитрий Бушуев:

      У меня всё скомпилировалось (как онлайн, так и на VS 2019 16.8.6) и выдало ожидаемый результат:
      https://onlinegdb.com/B1vrOJSfO

      1. Михаил:

        Дмитрий, у меня тоже все компилируется, проблема возникает после заливки скомпилированной прошивки в устройство. Но, на 19-й строчке моего примера у Wemos начинается когнитивный диссонанс ввиде переполнение стека и вываливается исключение 29 (попытка записи в область памяти недоступную для записи):

        21:55:14.004 ->
        21:55:14.004 -> Exception (29):
        21:55:14.004 -> epc1=0x40219fe1 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
        21:55:14.004 ->
        21:55:14.004 -> >>>stack>>>
        21:55:14.004 ->
        21:55:14.004 -> ctx: cont
        21:55:14.004 -> sp: 3ffff700 end: 3fffffc0 offset: 01a0
        21:55:14.004 -> 3ffff8a0: 3ffe0030 00000450 3ffffd24 4020fd02
        21:55:14.004 -> 3ffff8b0: 3ffef990 00000468 3ffefe84 3ffff8e0
        21:55:14.098 -> 3ffff8c0: 3ffef6b4 00000438 3ffef6b4 4020fe0e
        и т.п. и т.д.
        Причем при записи значения в аналогичный массив внутри этой же программы, но не вложенный в структуру, переполнения не происходит. Поэтому я и посчитал, что возможно я неправильно обращаюсь к такому массиву…

    2. Артем:

      Похоже на то, что вы перепутали row и columns местами.

  4. Vlad:

    Мгер, Кто вам такое сказал ?
    Ваш код с обычным двумерным массивом легко справляется.
    Если вы не знаете как это сделать, то это не значит что невозможно.
    Не вводите людей в заблуждение

  5. Георгий:

    Кстати, начиная с C++17 компилятор может вывести не только длину, но и тип массива:

  6. Снова я:

    Можете объяснить этот момент?

    Почему елементам 2 и 3, а не 1 и 2 ?

    1. Мгер:

      тут все что не заполнено, получает значение по умолчанию.
      так же как и с обычным массивом:

  7. Антон:

    "std::array — это отличная замена стандартных фиксированных массивов. Они более эффективны, так как используют меньше памяти." Это вдруг почему?

    1. Георгий:

      Не по чему. На деле, std::array и обычный массив подуцируют один и тот же ассемблерный код (проверял в VS). С std::array удобнее присваивание целого массива, передача в функции, копирование и т. д. (с обычными массивами тут большие проблемы). По эффективности они абсолютно идентичны

  8. Арман:

    Ну конечно array будет знать свой размер , если указывать его в параметрах :

    А если функция должна работать с разными array. Передавать размер как аргумент или я что то не понял?

    1. Мгер:

      Не совсем то что ты хочешь, но вот еще как можно сделать:

    2. Виталий:

      В общем если массивы одинаковые по типу и размеру, то прокатывает. А вот если разные то нет.

  9. Андрей:

    А как на счет двумерного массива?

    1. Фото аватара Юрий:

      Двумерный массив объявляется как:

      Здесь myarray — это название вашего двумерного массива, а сам двумерный массив создается с помощью вложенного массива (т.е. один внутри другого).

      1. Vlad:

        Это очень неудобно. Тут лучше использовать обычные квадратные скобки

        1. Мгер:

          с обычным массивом много что нельзя сделать. Например это:

      2. alex_1988:

        Покажите синтаксис использования функции at() для двухмерного массива и почему size() не показывает суммарную длину всех ячеек двухмерного массива?

        1. Сергей:

          Спасибо alex_1988 за вопросы.
          И конечно автору сайта за качественный перевод.

          Синтаксис использования функции at() в двумерных массивах выглядит так: myarray2.at().at()

          Но есть особенность, например:

          Здесь сначала идет проверка последнего (второго) индекса массива myarray2 (объявлено 5, проверяем от 0 до 4), а затем предпоследнего (первого) индекса массива myarray2 (объявлено 10, проверяем от 0 до 9).

      3. Евгений:

        А как многомерный std::array инициализировать?

        Пишет, слишком много инициализаторов.
        А кажется, что в самый раз

        1. Виталий:

Добавить комментарий для Андрей Отменить ответ

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