Рейтинговые книги
Читем онлайн Освой самостоятельно С++ за 21 день. - Джесс Либерти

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 69 70 71 72 73 74 75 76 77 ... 170

4. Запишите виртуальный конструктор-копировщик для класса Square, взятого из упражнения 3.

5. Жучки: что неправильно в следующем программном коде?

void SomeFunction(Shape);

Shape * pRect = new Rectangle;

SoneFunction(*pRect);

6. Жучки: что неправильно в следующем программном коде?

class Shape()

{

   public:

      Shape();

      virtual -Shape();

      virtual Shape(const Shape&);

};

День 12-й. Массивы и связанные листы

В программах, представленных в предыдущей главе, объявлялись одиночные объекты типов int, char и др. Но часто возникает необходимость создать коллекцию объектов, например 20 значений типа int или кучу объектов типа CAT. Сегодня вы узнаете:

• Что представляет собой массив и как его объявить

• Что такое строки и как их создавать с помощью массивов символов

• Какие существуют отношения между массивами и указателями

• Каковы особенности математических операций с указателями, связанными с массивами

Что такое массивы

Массивы представляют собой коллекции данных одного типа, сохраненные в памяти компьютера. Каждая единица данных называется элементом массива.

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

long LongArray[25];

В этом примере объявляется массив под именем LongArray, который может содержать 25 элементов типа long int. Обнаружив подобную запись, компилятор резервирует в памяти компьютера место, чтобы сохранить 25 элементов указанного типа. Поскольку для сохранения одного значения типа long int требуется четыре байта памяти, то для заданного массива компилятор выделит цельную область памяти размером 100 байт (рис. 12.1).

Элементы массива

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

разом: arrayName[0]. Если использовать пример массива, приведенный в предыдущем разделе, то обращение к первому элементу массива будет выглядеть так: LongArray[0], а ко второму — LongArray[1] и т.д. В общем виде, если объявлен массив Массив[n], то к его элементам можно обращаться, указывая индекс от Массив[0] до Массив[n-1].

Рис. 12.1. Объявление массива

Так, в нашем примере массива LongArray[25] для обращения к элементам используются индексы от LongArray[0]flo LongArray[24]. В листинге 12.1 показано объявление массива целых чисел из пяти элементов и заполнение его данными.

Листинг 12.1. Использование массива целых чисел 

1: //Листинг 12.1. Массивы

2: #include <iostream.h>

3:

4: int main()

5: {

6:    int myArray[5];

7:    int i;

8:    for ( i=0; i<5; i++) // 0-4

9:    {

10:      cout << "Value for myArray[" << i << "]: ";

11:      cin >> myArray[i];

12:   }

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

14:   cout << i << ": " << myArray[i] << "n";

15:   return 0;

16: }

Результат:

Value for myArray[0] 3

Value for myArray[1] 6

Value for myArray[2] 9

Value for myArray[3] 12

Value for myArray[4] 15

0: 3

1: 6

2: 9

3: 12

4: 15

Анализ: В строке 6 объявляется массив myArray, который может содержать пять целочисленных значений. В строке 8 начинается цикл от 0 до 4, в котором задаются допустимые индексы созданного массива. Пользователю предлагается ввести свое значение для текущего элемента массива, после чего это значение сохраняется в компьютере по адресу, отведенному компилятором для данного элемента массива.

Первое значение сохраняется по адресу, указанному в имени массива с нулевым сдвигом, — myArray[0], второе — в ячейке myАггау[1]и т.д. Второй цикл программы выводит сохраненные значения на экран.

Примечание:Следует запомнить, что отсчет элементов массива начинается с 0, а не с 1. Это источник частых ошибок новичков в программах на C++. Если используется массив, состоящий из 10 элементов, то для обращения к элементам массива используются индексы от ArrayName[0] до ArrayName[9]. Обращение ArrayName[10] будет ошибочным.

Вывод данных за пределами массива

При записи данных в массив компилятор вычисляет адрес соответствующего элемента, основываясь на размере элемента и указанном сдвиге относительно первого элемента. Предположим, что некоторое значение записывается в шестой элемент рассмотренного нами ранее массива LongArray, для чего используется индекс LongArray[5]. Компилятор умножит указанное значение сдвига 5 на размер элемента (в нашем примере 4 байт) и получит 20 байт. Затем компилятор вычислит адрес шестого элемента массива, добавив к адресу массива 20 байт сдвига и запишет введенное значение по этому адресу.

Если при записи данных будет указан индекс LongArray[50], то компилятор не сможет самостоятельно определить, что такого элемента массива просто не существует. Компилятор вычислит, что такой элемент должен находиться по адресу, сдвинутому на 200 байт относительно адреса первого элемента массива, и запишет в эту ячейку памяти введенное значение. В связи с тем, что выбранная область памяти может принадлежать любой другой переменной, результат такой операции для работы программы непредсказуем. Если вам повезет, то программа зависнет сразу же. Если вы неудачник, то программа продолжит работу и через некоторое время выдаст вам совершенно неожиданный результат. Такие ошибки очень сложно локализовать, поскольку строка, где проявляется ошибка, и строка, где ошибка была допущена в программе, могут далеко отстоять друг от друга.

Компилятор ведет себя, как слепой человек, отмеряющий расстояние от дома к дому шагами. Он стоит возле первого дома на улице с адресом ГлавнаяУлица[0] и спрашивает вас, куда ему идти. Если будет дано указание следовать до шестого дома, то наш человек-компилятор станет размышлять следующим образом: "Чтобы добраться до шестого дома, от этого дома нужно пройти еще пять домов. Чтобы пройти один дом, нужно сделать четыре больших шага. Следовательно, нужно сделать 20 больших шагов." Если вы поставите задачу идти до дома ГлавнаяУлица[100], а на этой улице есть только 25 домов, то компилятор послушно начнет отмерять шаги и даже не заметит, что улица закончилась и началась проезжая часть с несущимися машинами. Поэтому, посылая компилятор по адресу, помните, что вся ответственность за последствия лежит только на вас.

Возможный результат ошибочной записи за пределы массива показан в листинге 12.2.

Предупреждение:Ни в коем случае не запускайте эту программу у себя на компьютере. Она может привести к поломке системы.

Листинг 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] << "nn";

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] << "nn";

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.

1 ... 69 70 71 72 73 74 75 76 77 ... 170
На этой странице вы можете бесплатно читать книгу Освой самостоятельно С++ за 21 день. - Джесс Либерти бесплатно.

Оставить комментарий