Массивы и указатели
Массив (array) — это тип данных, который используется для представления последовательности однородных значений. Доступ к элементам массива производится с помощью индексов. В C++ возможны массивы любых типов, включая массивы массивов. Объявление типичного массива используется для выделения памяти, начиная с базового адреса. На самом деле имя массива является постоянным указателем, инициализованным этим базовым адресом.
Чтобы проиллюстрировать эти идеи, давайте напишем небольшую программу, которая заполняет массив, выводит значения и суммирует элементы:
// Простая обработка массива
#include <iostream.h>
const int SIZE = 5;
int main ( )
{ int a [SIZE]; // резервируем место для а[0],.....,а[4]
int i, sum=0;
for (i = 0; i < SIZE; ++ i) {
a[i]=i * i;
cout << “a [“ << i <<”] = ” << a[ i ] << “ “ ;
sum + = a[ i ];
}
cout « "\n сумма = “ << sum << endl;
}
Вывод этой программы выглядит так:
а[0] = 0 а[1] = 1 а[2] =4 a[3]=9 a[4]=16
sum = 30
Вышеприведенному массиву требуется память для хранения пяти целых значений. Так если а [ 0 ] хранится по адресу 1000, то в системе, отводящей для хранения целого 2 байта, остальные элементы массива последовательно размещаются по адресам 1002, 1004, 1006 и 1008.
Индексирование
Предположим, было сделано объявление вида:
Int I, а [размер];
Тогда для доступа к элементу массива мы можем написать: а[ i ]. В общем случае эта запись выглядит так: а [выражение], где выражение — это целое выражение. Указанное выражение мы называем индексом (subscript или index) элемента массива а. В C++ значение индекса должно лежать в диапазоне от 0 до размер -1. Программист всегда должен быть уверен, что все индексы находятся в пределах этих границ.
Многомерные массивы
Язык C++ допускает массивы любого типа, включая массивы массивов. С двумя парами квадратных скобок мы имеем двухмерный массив. Продолжая в том же духе, можно получить массивы более высокой размерности. С каждой новой парой квадратных скобок мы наращиваем размерность массива.
Объявления массивов
int a [100]; одномерный массив
int b [3][5]; двухмерный массив
int c [7][9][2]; трехмерный массив
Любой k-мерный массив имеет размер по каждому из своих k измерений. В приведенной таблице b содержит 3 Х 5 элементов, а c – 7 х 9 х 2 элементов. Начиная с базового адреса массива все элементы хранятся в памяти последовательно.
Инициализация
Массив может быть инициализован с помощью заключенного в фигурные скобки списка выражений, разделенных запятыми:
int а[4] = {9, 8, 7}; //а[0]=9, а[1]=8, а[2]=7
Когда список инициализаторов короче размера массива, остальные элементы инициализуются нулем. Неинициализованные внешние и статические массивы автоматически инициализуются нулем. Однако автоматические массивы открываются с неопределенными значениями.
Массиву, объявленному с явным списком инициализаторов, но без задающего его размер выражения, дается размер, соответствующий количеству инициализаторов:
char laura[ ] = {‘l’, ‘m’, ‘p’};
равнозначно
char laura[3] = {‘l’, ‘m’, ‘p’};
Многомерный массив может быть инициализован заключенным в фигурные скобки списком инициализаторов, причем каждый ряд инициализуется своим списком в фигурных скобках:
int а[2][3] = { {1, 2, 3}, {4, 5, 6} };
//то же, что {1, 2, 3, 4, 5, 6}
char name[3][9] = { "laura", "michele", "pohl" };
//завершенные '\0'
Связь между массивами и указателями
Имя массива само по себе является адресом или значением указателя. Массивы и указатели почти идентичны в смысле их использования для доступа к памяти. Однако существуют едва уловимые, но важные различия. Указатель — это переменная, принимающая в качестве значения адрес. А имя массива является конкретным фиксированным адресом, который может пониматься как постоянный указатель на первый (с индексом 0) элемент массива. Допустим, что мы написали следующее объявление:const int N = 100;
int a[N], *p;
и что система назначила байты памяти 300, 304, 308, ... , 696 в качестве адресов для хранения а[0],а[1],а[2],...,а[99] соответственно; при этом адрес 300 стал базовым адресом массива а. Мы полагаем, что каждый байт памяти адресуем, а для хранения целого используется четыре байта. Две инструкции
p = a; и p = &a[0];
являются равнозначными и присваивают p значение 300. Арифметика указателей предлагает альтернативу индексированию массивов. Две инструкции
p = a + 1; и p = & a [1];
равнозначны и присваивают p значение 304. Предположив, что элементам a были присвоены значения, мы можем использовать следующий код для суммирования массива:
sum = 0;
for (p = a; p < & a[N]; ++p)
sum += *p;
это равносильно
sum = 0;
for (i = 0; i < N; ++i)
sum += a[i];
Еще один способ суммирования массива:
sum = 0;
for (i=0; i<N; ++i)
sum += *(a +i)
Также как выражение *(a+i) равносильно а[i], выражение * (p + i ) равносильно p[i].
Мы не можем изменить адреса, отметим, что sizeof (a) и sizeof (p) различны. Первое выражение дает размер всего массива, тогда как второе – размер выражения – указателя.