Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих.pdf
Скачиваний:
183
Добавлен:
01.05.2014
Размер:
3.97 Mб
Скачать

3.9.1. Многомерные массивы

В С++ есть возможность использовать многомерные массивы, при объявлении которых необходимо указать правую границу каждого измерения в отдельных квадратных скобках. Вот определение двумерного массива:

int ia[ 4 ][ 3 ];

Первая величина (4) задает количество строк, вторая (3) – количество столбцов. Объект ia определен как массив из четырех строк по три элемента в каждой. Многомерные

int ia[ 4 ][ 3 ] =

{

{0, 1, 2 },

{3, 4, 5 },

{6, 7, 8 },

{9, 10, 11 }

массивы тоже могут быть инициализированы:

};

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

int ia[4][3] = { 0,1,2,3,4,5,6,7,8,9,10,11 };

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

int ia[ 4 ][ 3 ] = { {0}, {3}, {6}, {9} };

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

int ia[ 4 ][ 3 ] = { 0, 3, 6, 9 };

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

int main()

{

const int rowSize = 4; const int colSize = 3;

int ia[ rowSize ][ colSize ];

for ( int = 0; i < rowSize; ++i )

for ( int j = 0; j < colSize; +

+j )

ia[ i ][ j ] = i + j j;

}

Конструкция

ia[ 1, 2 ]

является допустимой с точки зрения синтаксиса С++, однако означает совсем не то, чего ждет неопытный программист. Это отнюдь не объявление двумерного массива 1 на 2. Агрегат в квадратных скобках – это список выражений через запятую, результатом которого будет последнее значение 2 (см. оператор “запятая” в разделе 4.2). Поэтому объявление ia[1,2] эквивалентно ia[2]. Это еще одна возможность допустить ошибку.

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

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

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

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

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;

предыдущем разделе, или с помощью указателей. Например:

while ( pbegin != pend )

{

cout << *pbegin <<; ++pbegin;

int *pend = ia + 9;

}

Указатель 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. Есть способ снять и это ограничение: преобразовать данную

#inc1ude <iostream>

template <c1ass e1emType>

void print( elemType *pbegin, elemType *pend )

{

while ( pbegin != pend ) { cout << *pbegin << ' ';

++pbegin;

}

функцию в шаблон (шаблоны были вкратце представлены в разделе 2.5):

}

int main()

 

{

2, 3, 5, 8, 13,

int ia[9] = { 0, 1, 1,

21 };

6.28, 12.56, 25.12 };

double da[4] = { 3.14,

string sa[3] = { "piglet", "eeyore", "pooh" };

print( ia, ia+9 ); print( da, da+4 ); print( sa, sa+3 );

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

}

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