Урок 93. Указатели на указатели

   ⁄ 

 Обновлено 3 Ноя 2017

  ⁄   

Этот урок не является обязательным, он для тех, кто хочет узнать больше о C++. На прогресс в продвижении изучения C++ этот материал не влияет.

Указатель на указатель – это именно то, что вы подумали: указатель, который содержит адрес другого указателя.

Указатели на указатели

Обычный указатель на int объявляется с использованием одной звёздочки:

Указатель на указатель на int объявляется с использованием двух звёздочек:

Указатель на указатель работает подобно обычному указателю — вы можете его разыменовать для получения значения, на которое он указывает. И поскольку этим значением является другой указатель, то для получения исходного значения вам нужно будет сделать разыменование еще раз. Их следует выполнять последовательно:

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

7
7

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

Это связано с тем, что оператор адреса (оператор &) требует l-value, но &value – это r-value.

Однако указателю на указатель можно задать значение null:

Массивы указателей

Указатели на указатели имеют несколько применений. Наиболее используемое — динамическое выделение массива указателей:

Это тот же обычный динамически выделенный массив, за исключением того, что элементами являются указатели на тип int, а не значения типа int.

Двумерные динамически выделенные массивы

Другим распространенным применением указателей на указатели является динамическое выделение многомерных массивов.

В отличие от двумерного фиксированного массива, который можно легко объявить следующим образом:

Динамическое выделение двумерного массива немного отличается. У вас может возникнуть соблазн написать что-то вроде этого:

Получите ошибку.

Здесь есть два возможных решения. Если правый индекс является константой типа compile-time, то вы можете сделать следующее:

Скобки здесь требуются для обеспечения правильности выполнения операций (тема приоритета). В C++11 хорошей идеей будет использовать ключевое слово auto для автоматического определения типа данных:

К сожалению, это относительно простое решение не работает, если правый индекс не является константой типа compile-time. В таком случае всё немного усложняется. Сначала мы выделяем массив указателей (как в примере выше). Затем перебираем каждый элемент массива указателей и выделяем динамический массив для каждого элемента этого массива. Итого наш динамический двумерный массив — это динамический одномерный массив динамических одномерных массивов!

Затем доступ к элементам массива выполняется как обычно:

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

В примере выше array[0] — это массив длиной 1, array[1] — массив длиной 2 и т.д.

Для освобождения памяти динамически выделенного двумерного массива (который создавался с помощью этого способа) также требуется цикл:

Обратите внимание, мы удаляем массив в обратном порядке, в котором его создавали. Если мы удалим массив перед элементами массива, тогда нам придется получать доступ к освобожденной памяти для удаления элементов массива. А это в свою очередь приведет к неожиданным результатам.

Поскольку процесс выделения и освобождения двумерных массивов является несколько запутанным (можно легко наделать ошибок), то часто проще «сгладить» двумерный массив (размером x на y) в одномерный массив размером x * y:

Затем простая математика используется для конвертации индексов строки и столбца прямоугольного двумерного массива в один индекс одномерного массива:

Указатель на указатель на указатель на указатель и т.д.

Также можно объявить указатель на указатель на указатель:

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

Вы даже можете объявить указатель на указатель на указатель на указатель:

Или даже еще больше, если захотите.

Однако на практике такие указатели редко когда используются.

Итого

Мы рекомендуем избегать использования указателей на указатели, разве что в крайних случаях, так как они сложны в использовании и потенциально опасны. Достаточно легко разыменовать нулевой или висячий указатель в ситуациях с использованием обычных указателей — вдвое легче это сделать в ситуациях с указателем на указатель, поскольку для получения исходного значения требуется выполнить двойное разыменование!

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

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (3 оценок, среднее: 4,67 из 5)
Загрузка...
Поделиться в:
Подписаться на обновления:

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

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