Листинг 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; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!