Урок 82. Указатели и массивы

   ⁄ 

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

  ⁄   

Указатели и массивы на самом деле очень связаны в C++.

Сходства между указателями и фиксированными массивами

В уроке 74 о массивах мы узнали, как определить фиксированный массив:

Для нас это массив из 4 целых чисел, но для компилятора array является переменной типа int[4]. Мы знаем значения array[0], array[1], array[2] и array[3]: 5, 8, 6 и 4 соответственно. Но какое значение имеет array?

Переменная array содержит адрес первого элемента в массиве, как если бы это был указатель! Например:

Результат на компьютере автора:

The array has address: 004BF968
Element 0 has address: 004BF968

Обратите внимание, адрес, хранящийся в переменной array, является адресом первого элемента массива.

Распространенная ошибка думать, что переменная array и указатель на array  — идентичны. Это не так. Хотя оба указывают на первый элемент в массиве, информация о типе у них разная. В примере выше тип array — int[4], тогда как тип указателя на массив — int *.

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

Однако и это не является столь весомым аргументом, чтобы рассматривать фиксированные массивы и указатели как разные значения.

Например, мы можем разыменовать массив, чтобы получить значение первого элемента:

Обратите внимание, мы не разыменовываем фактический массив. Массив (типа int[4]) неявно преобразуется в указатель (типа int *), и мы разыменуем указатель, который указывает на значение первого элемента в массиве.

Также мы можем присвоить указатель на array:

Это работает из-за того, что переменная array распадается на указатель типа int *, а тип нашего указателя такой же (int *).

Различия между указателями и фиксированными массивами

Однако есть случаи, когда разница между фиксированными массивами и указателями имеет значение.

Основное различие возникает при использовании оператора sizeof(). При использовании в фиксированном массиве sizeof возвращает размер всего массива (длина массива * размер элемента). При использовании с указателем sizeof возвращает размер адреса памяти (в байтах). Например:

Результат:

16
4

Фиксированный массив знает свою длину. Указатель на массив — нет.

Второе различие возникает при использовании оператора адреса (&). Используя адрес указателя мы получаем адрес памяти переменной указателя. Используя адрес массива, возвращается указатель на целый массив. Этот указатель также указывает на первый элемент массива, но информация о типе отличается (в примере выше int (*) [4]). Вряд ли вам когда-нибудь понадобится это использовать.

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

В уроке 75 мы упоминали, что из-за того, что копирование больших массивов является очень затратным при передаче в функцию, C++ не копирует массив. При передаче массива в качестве аргумента функции, он распадется на указатель, а указатель передастся в функцию:

Результат:

32
4

Обратите внимание, результат будет такой же, даже если параметр объявить в виде фиксированного массива:

Результат:

32
4

В примере выше C++ неявно конвертирует параметр из синтаксиса массива ([]) в синтаксис указателя (*). Это означает, что следующие два объявления функции идентичны:

Некоторые программисты предпочитают использовать синтаксис [], так как он дает понять, что функция ожидает массив, а не указатель на значение. Однако, в большинстве случаев, поскольку указатель не знает, насколько велик массив — вам придется передавать размер массива в качестве отдельного параметра в любом случае (строки являются исключением, так как они нуль-терминированные).

Мы рекомендуем использовать синтаксис указателя, поскольку он дает понять, что параметр будет обрабатываться как указатель, а не как фиксированный массив, и определенные операции, такие как sizeof(), будут выполняться с параметром, как с указателем (а не как с фиксированным массивом).

Совет: Используйте синтаксис указателя (*) вместо синтаксиса массива ([]) при передаче массивов в качестве параметров в функции.

Вступление. Передача по адресу

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

Результат:

Element 0 has value: 1
Element 0 has value: 5

При вызове changeArray(), массив распадется на указатель, а значение этого указателя (адрес памяти первого элемента в массиве) скопируется в параметр ptr функции changeArray(). Хотя значение ptr в функции является копией адреса массива, ptr все равно указывает на фактический массив (а не на копию!). Следовательно, при разыменовании ptr, разыменовывается и фактический массив!

Этот феномен работает так же и с указателями на значения не из массива. Тему «Передача по ссылке» мы рассмотрим более подробно в следующей главе.

Массивы в структурах и классах не распадаются

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

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

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

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

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

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