Ошибки подсчета столбцов для забора

Ошибки с записью данных за пределы массива случаются настолько часто, что для них используется особый термин — ошибки подсчета столбов для забора. Такое странное название было придумано по аналогии с одной житейской проблемой — подсчетом, сколько столбов нужно вкопать, чтобы установить 10-метровый забор, если расстояние между столбами должно быть 1 м. Многие не задумываясь отвечают — десять. Вот если бы задумались и посчитали, то нашли бы правильный ответ — одиннадцать. Если вы не поняли почему, посмотрите на рис. 12.2.

Ошибка на единицу при установке индекса в обращении к массиву может стоить программе жизни. Нужно время, чтобы начинающий программист привык, что при обращении к массиву, состоящему из 25 элементов, индекс не может превышать значение 24 и отсчет начинается с 0, а не с 1. (Некоторые программисты потом настолько к этому привыкают, что в лифте нажимают на кнопку 4, когда им нужно подняться на пятый этаж.)

Примечание: Иногда элемент массива ИмяМассива[0] называют нулевым, а не первым. Но и в этом случае легко запутаться. Если элемент ИмяМассива[0] нулевой, то каким тогда будет элемент ИмяМассива[1]? Первым или вторым? И так далее... Каким будет элемент ИмяМассива[24]— двадцать четвертым или двадцать пятым? Правильно будет считать элемент ИмяМассива[0] первым, имеющим нулевой сдвиг.

Инициализация массива

Инициализацию массива базового типа (например, int или char) можно проводить одновременно с его объявлением. Для этого за выражением объявления массива нужно установить знак равенства (=) и в фигурных скобках список значений элементов массива, разделенных запятыми. Например:

int IntegerArray[5] = {10, 20, 30, 40, 50>;

В этом примере объявляется массив целых чисел IntegerArray и элементу IntegerArray[0] присваивается значение 10, элементу IntegerArray[ 1 ] — 20 И т.д.

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

int IntegerArray[] = {10. 20, 30, 40, 50};

Рис. 12.2. Ошибка подсчета столбов для забора

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

const USHORT IntegorArrayLength;

IntegerArrayLength = sizeof(IntegerArray)/sizeof(IntegerArray[0]);

В этом примере число элементов массива определяется как отношение размера массива в байтах к размеру одного элемента. Результат отношения сохраняется в переменной IntegerArrayLength типа const USHORT, которая была объявлена строкой выше.

Нельзя указывать в списке больше значений, чем заданное количество элементов массива, Так, следующее выражение вызовет показ компилятором сообщения об ошибке, поскольку массиву, состоящему из пяти элементов, пытаются присвоить шесть значений:

int IntegerArray[5] = {10, 20, 30, 40, 50, 60);

В то же время следующее выражение не будет ошибочным:

int IntegerArray[5] = {10, 20};

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

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

Не рекомендуется: Не записывайте данные за пределы массива.

Объявление массивов

Массиву можно присвоить любое имя, но оно должно отличаться от имени всех других переменных и массивов в пределах видимости зтого массива. Так, нельзя объявить массив myCats[5], если в программе ранее уже была объявлена переменная myCats.

Размер массива при объявлении можно задать как числом, так и с помощью константы или перечисления, как показано в листинге 12,3.

Листинг 12.3. Использование константы и перечисления при объявлении массива

1: // Листинг 12.3.

2: // Установка размера массива с помощью константы и перечисления

3:

4: #include <iostream.h>

5: int main()

6: {

7: enum WeekDays { Sun, Mon, Tue,

8: Wed, Thu, Fri, Sat, DaysInWeek };

9: int ArrayWeek[DaysInWeek] = { 10, 20, 30, 40, 50, 60, 70 }

10:

11: cout << "The value at Tuesday is " << ArrayWeek[Tue];

12: return 0;

13: }

Результат:

The value at Tuesday is 30

Анализ: В строке 7 объявляется перечисление WeekDays, содержащее восемь членов. Воскресенью (Sunday) соответствует значеню 0, а константе DaysInWeek — значение 7.

В строке 11 константа перечисления Tue используется в качестве указателя на элемент массива. Поскольку константе Tue соответствует значение 2, то в строке 11 возвращается и выводится на печать значение третьего элемента массива ArrayWeek[2].

Массивы

Чтобы объявить массив, сначала нужно указать тип объектов, которые будут в нем сохранены, затем определить имя массива и задать размер массива. Размер определяет, сколько объектов заданного типа можно сохранить в данном массиве.

Пример 1:

intMyIntegerArray[90];

Пример 2:

long * Array0fPointersToLogs[100];

Чтобы получить доступ к элементам массива, используется оператор индексирования. Пример 1:

Int theNinethInteger = MyIntegerArray[8];

Пример 2:

long * pLong = Array0fPointersToLogs[8];

Отсчет индексов массива ведется с нуля. Поэтому, для обращения к массиву, содержащему n элементов, используются индексы от 0 до n-1.

Массивы объектов

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

Получение доступа к данным переменных-членов объекта, сохраненного в массиве, идет в два этапа. Сначала с помощью оператора индексирования ([]) нужно указать элемент массива, а затем обратиться к конкретной переменной-члену с помощью оператора прямого обращения к члену класса (.). В листинге 12.4 показано создание массива для пяти объектов типа CAT.

Листинг 12.4. Создание массива объектов

1: // Листинг 12.4. Массив объектов

2:

3: #include <iostream.h>

4:

5: class CAT

6: {

7: public:

8: CAT() { itsAge = 1; itsWeight=5; }

9: ~CAT() { }

10: int GetAge() const { return itsAge; }

11: int GetWeight() const { return itsWeight; }

12: void SetAge(int age) { itsAge = age; }

13:

14: private:

15: int itsAge;

16: int itsWeight;

17: };

18:

19: int main()

20: {

21: CAT Litter[5];

22: int i;

23: for (i = 0; i < 5; i++)

24: Litter[i].SetAge(2*i +1);

25:

26: for (i = 0; i < 5; i++)

27: {

28: cout << "cat #" << i + 1<< ": ";

29: cout << Litter[i].GetAge() << endl;

30: }

31: return 0;

32: }

Результат:

cat #1: 1

cat #2: 3

cat #3: 5

cat #4: 7

cat #5: 9

Анализ: В строках 5—17 объявляется класс CAT. Чтобы объекты класса CAT могли создаваться при объявлении массива, в этом классе должен использоваться конструктор, заданный по умолчанию. Вспомните, что если в классе создан какой- нибудь другой конструктор, то конструктор по умолчанию не будет предоставляться компилятором и вам придется создавать его самим.

Первый цикл for (строки 23 и 24) заносит значения возраста кошек в объекты класса, сохраненные в массиве. Следующий цикл for (строки 26—30) обращается к каждомуобъек- ту, являющемуся элементом массива, и вызывает для выбранного объекта метод GetAge().

Чтобы применить метод GetAge() для объекта, являющегося элементом массива, используются последовательно операторы индексации ([]) и прямого доступа к члену класса (.), а также вызов функции-члена.

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

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

Хорошим примерным двухмерного массива является шахматная доска, состоящая из клеток, собранных в восемь рядов и восемь столбцов (рис. 12.3).

Рис. 12.3. Шахматная доска и двухмерный массив

Предположим, что в программе объявлен класс SQUARE. Объявление двухмерного массива Board для сохранения объектов этого класса будет выглядеть следующим образом:

SQUARE Board[8][8];

Эти же объекты можно было сохранить в одномерном массиве с 64 элементами:

SGUARE Board[64];

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

Board[0][3];

В этом примере предполагается, что первый индекс будет контролировать нумерацию рядов, а второй — столбцов. Соответствие элементов массива клеткам шахматной доски наглядно показано на рис. 12.3.

Наши рекомендации