C++ для начинающих

3.9.2. Взаимосвязь массивов и указателей

Если мы имеем определение массива:

 int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };

то что означает простое указание его имени в программе?

ia;

Использование идентификатора массива в программе эквивалентно указанию адреса его первого элемента:

ia;
&ia[0]

Аналогично обратиться к значению первого элемента массива можно двумя способами:

// оба выражения возвращают первый элемент
*ia;
ia[0];

Чтобы взять адрес второго элемента массива, мы должны написать:

&ia[1];

Как мы уже упоминали раньше, выражение

 ia+1;

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

*(ia+1);
ia[1];

Отметим разницу в выражениях:

*ia+1
и
*(ia+1);

Операция разыменования имеет более высокий приоритет, чем операция сложения (о приоритетах операций говорится в разделе 4.13). Поэтому первое выражение сначала разыменовывает переменную ia и получает первый элемент массива, а затем прибавляет к нему 1. Второе же выражение доставляет значение второго элемента.

Проход по массиву можно осуществлять с помощью индекса, как мы делали это в предыдущем разделе, или с помощью указателей. Например:

#include <iostream>
int main()
{
   int ia[9] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };
   int *pbegin = ia;
   int *pend = ia + 9;
   while ( pbegin != pend ) {
      cout << *pbegin <<;
      ++pbegin;
   }
}

Указатель pbegin инициализируется адресом первого элемента массива. Каждый проход по циклу увеличивает этот указатель на 1, что означает смещение его на следующий элемент. Как понять, где остановиться? В нашем примере мы определили второй указатель pend и инициализировали его адресом, следующим за последним элементом массива ia. Как только значение pbegin станет равным pend, мы узнаем, что массив кончился. Перепишем эту программу так, чтобы начало и конец массива передавались параметрами в некую обобщенную функцию, которая умеет печатать массив любого размера:

#inc1ude <iostream>
void ia_print( int *pbegin, int *pend )
{
while ( pbegin != pend ) {
cout << *pbegin << ' ';
++pbegin;
}
} int main()
{
int ia[9] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };
ia_print( ia, ia + 9 );
}

Наша функция стала более универсальной, однако, она умеет работать только с массивами типа int. Есть способ снять и это ограничение: преобразовать данную функцию в шаблон (шаблоны были вкратце представлены в разделе 2.5):

#inc1ude <iostream>
template <c1ass e1emType>
void print( elemType *pbegin, elemType *pend )
{
   while ( pbegin != pend ) {
      cout << *pbegin << ' ';
      ++pbegin;
   }
}

Теперь мы можем вызывать нашу функцию print() для печати массивов любого типа:

int main()
{
   int ia[9] = { 0, 1, 1, 2, 3, 5, 8, 13, 21 };
   double da[4] = { 3.14, 6.28, 12.56, 25.12 };
   string sa[3] = { "piglet", "eeyore", "pooh" };
   print( ia, ia+9 );
print( da, da+4 );
print( sa, sa+3 );
}

Мы написали обобщенную функцию. Стандартная библиотека предоставляет набор обобщенных алгоритмов (мы уже упоминали об этом в разделе 3.4), реализованных подобным образом. Параметрами таких функций являются указатели на начало и конец массива, с которым они производят определенные действия. Вот, например, как выглядят вызовы обобщенного алгоритма сортировки:

#include <a1gorithm>
int main()
{
   int ia[6] = { 107, 28, 3, 47, 104, 76 };

   string sa[3] = { "piglet", "eeyore", "pooh" };
   sort( ia, ia+6 );
sort( sa, sa+3 );
};

(Мы подробно остановимся на обобщенных алгоритмах в главе 12; в Приложении будут приведены примеры их использования.)
В стандартной библиотеке С++ содержится набор классов, которые инкапсулируют использование контейнеров и указателей. (Об этом говорилось в разделе 2.8.) В следующем разделе мы займемся стандартным контейнерным типом vector, являющимся объектно-ориентированной реализацией массива.

Назад   Вперед
Содержание




Нет комментариев.



Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: