Листинг 12.2. Запись за пределы массива



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

2: // Пример того, что может произойти при записи

3: // за пределы массива

4:

5: #include <iostream.h>

6: int main()

7: {

8: // часовые

9: long sentinelOne[3];

10: long TargetArray[25]; // массив для записи данных

11: long sentinelTwo[3];

12: int i;

13: for (i=0; i<3; i++)

14: sentinelOne[i] = sentinelTwo[i] = 0;

15:

16: for (i=0; i<25; i++)

17: TargetArray[i] = 0;

18:

19: cout << "Test 1: \n"; // test current values (should be 0)

20: cout << "TargetArray[0]: " << TargetArray[0] << "\n";

21: cout << "TargetArray[24]: " << TargetArray[24] << "\n\n";

22:

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

24: {

25: cout << "sentinelOne[" << i << "]: ";

26: cout << sentinelOne[i] << "\n";

27: cout << "sentinelTwo[" << i << "]: ";

28: cout << sentinelTwo[i]<< "\n";

29: }

30:

31: cout << "\nAssigning...";

32: for (i = 0; i<=25; i++)

33: TargetArray[i] = 20;

34:

35: cout << "\nTest 2: \n";

36: cout << "TargetArray[0]: " << TargetArray[0] << "\n";

37: cout << "TargetArray[24]: " << TargetArray[24] << "\n";

38: cout << "TargetArray[25]; " << TargetArray[25] << "\n\n";

39: for (i = 0; i<3; i++)

40: {

41: cout << "sentinelOne[" << i << "]: ";

42: cout << sintinel0ne[i]<< "\n";

43: cout << "sentinelTwo[" << i << "]: ";

44: cout << sentinelTwo[i]<< "\n";

45: }

46:

47: return 0;

48: }

 

Результат:

Test 1:

TargetArray[0]: 0

TargetArray[24]: 0

Sentinel0ne[0]: 0

SentinelTwo[0]: 0

SentinelOne[1]: 0

SentinelTwo[1]: 0

SentinelOne[2]: 0

SentinelTwo[2]: 0

Assigning...

Test 2:

TargetArray[0]: 20

TargetArray[24]: 20

TargetArray[25]: 20

Sentinel0ne[0]: 20

SentinelTwo[0]: 0

SentinelOne[1]: 0

SentinelTwo[1]: 0

SentinelOne[2]: 0

SentinelTwo[2]: 0

 

Анализ: В строках 9 и 11 объявляются два массива типа long по три элемента в каж- '" " ' дом, которые выполняют роль часовых вокруг массива TargetArray. Изначаль

но значения этих массивов устанавливаются в 0. Если будет записано значение в массив TargetArray по адресу, выходящему за пределы этого массива, то значения массивов- часовых изменятся. Одни компиляторы ведут отсчет по возрастающей от адреса массива, другие — по убывающей. Именно поэтому используется два вспомогательных массива, расположенных по обе стороны от целевого массива TargetArray.

В строках 19-29 проверяется равенство нулю значений элементов массивов-часовых (Test 1). В строке 33 элементу массива TargetArray присваивается значение 20, но при этом указан индекс 25, которому не соответствует ни один элемент массива TargetArray.

В строках 36-38 выводятся значения элементов массива TargetArray (Test 2). Обратите внимание, что обрашение к элементу массива TargetArray[25] проходит вполне успешно и возвращается присвоенное ранее значение 20. Но когда на экран выводятся значения массивов-часовых Sentinel0na и SentinelTwo, вдруг обнаруживается, что значение элемента массива 5entinelQne изменилось. Дело в том, что обращение к массиву TargetArray[25] ссылается на ту же ячейку памяти, что и элемент массива SentinelQne[Q]. Таким образом, записывая значения в несуществующий элемент массива TargetArray, программа изменяет значение элемента совсем другого массива.

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

В нашем примере размеры массивов были заданы значениями 3 и 25 в объявлении массивов. Гораздо безопаснее использовать для этого константы, объявленные где- нибудь в одном месте программы, чтобы программист мог легко контролировать размеры всех массивов в программе.

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

 

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

 

Ошибки с записью данных за пределы массива случаются настолько часто, что для них используется особый термин — ошибки подсчета столбов для забора. Такое странное название было придумано по аналогии с одной житейской проблемой — подсчетом, сколько столбов нужно вкопать, чтобы установить 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.


Дата добавления: 2019-02-12; просмотров: 232; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!