Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СТА (лекции+лабы) / Структуры и алгоритмы обработки данных.docx
Скачиваний:
97
Добавлен:
16.03.2016
Размер:
1.9 Mб
Скачать

Int main ()

{

   // Приглашение пользователя ко вводу

   std::cout << "Input integers, stop with Ctrl+Z: ";

   

   // Выделяем достаточно большой блок памяти

   int * pData = new int[ 50 ];

   // Пытаемся вводить числа с консоли одно за другим

   int numbersCount = 0;

   while ( true )

   {

       // Попытка ввода числа

       int temp;

       std::cin >> temp;

       // В случае успеха записываем число в массив, увеличивая счетчик

       if ( std::cin )

           pData[ numbersCount++ ] = temp;

       else

   // Иначе прерываем ввод

           break;

   }

   // Количество введенных данных сохранено в переменной numbersCount

   // Выводим данные в обратном порядке

   for ( int i = numbersCount - 1; i >= 0; i-- )

       std::cout << pData[ i ] << ' ';

   std::cout << std::endl;

   // Освобождение динамической памяти

   delete[] pData;

}

Программа будет прекрасно справляться с поставленной задачей до тех пор, пока количество вводимых чисел не превысит 50. Если же пользователь  введет больше чисел, например, 51, программа будет завершиться с крахом, поскольку произойдет запись за дозволенные границы выделенного массива (скорее всего, крах будет обнаружен в момент выполнения освобождения памяти инструкцией delete[]):

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

В результате данного рассуждения остается лишь прийти к следующим выводам:

  1. Программа должна обрабатывать количество данных, которое в нее вводит пользователь.

  2. Программа не должна завершаться фатально ни при каком вводе.

  3. Программа должна рационально расходовать память, выделяя необходимый ей минимум.

Каким же образом можно удовлетворить вышеуказанным требованиям к реализации?

Динамически растущие массивы (векторы)

Применим простейшую классическую структуру данных - динамически растущий массив. Во многих языках программирования эту структуру также называют вектором.

Основная идея такого массива состоит в резервировании некоторого небольшого объема памяти для данных и последующем автоматическом расширении как только текущий объем полностью заполняется. Для организации такой структуры необходимо ввести 3 переменные:

  • адрес начала текущего блока памяти для хранения (pData);

  • число выделенных ячеек в текущем блоке памяти (nAllocated);

  • число фактически занятых полезными данными ячеек в данный момент (nUsed).

Начальный выделяемый объем может быть произволен, разумной начальной настройкой является 8-10 ячеек. В начале часть выделенных ячеек использоваться не будет. Ячейки будут заполняться данными по мере ввода:

Затем, как только число занятых ячеек сравняется с числом выделенных, массив следует расширить вдвое. Прямого способа расширить выделенный массив язык С++ не предоставляет. Поэтому под расширением понимают выделение нового большего блока данных, перенос ранее введенных данных и освобождение старого блока. После расширения новый блок будет заполнен на 50%, а значит сможет принять еще столько же входных данных:

Продемонстрируем данную идею модифицировав часть предыдущей версии программы:

#include <iostream>

#include <cstring>