- •Ста: Лекция №1 - Введение. Данные в памяти программ
- •Введение
- •Модель памяти в прикладных программах
- •Intmain ()
- •Int main ()
- •Int main ()
- •Int main ()
- •Char buf[ 2000000 ];
- •Return f();// бесконечно долго вызываем сами себя // вызываем переполнение стека через время
- •Int main ()
- •Return f();
- •Int main ()
- •Delete[]p;// Освобождаем память
- •Int main ()
- •Int a[ n ];// ошибка, размер нельзя вычислить во время компиляции
- •Delete[]p;
- •Int main ()
- •Проблема фиксированного размера массивов
- •Intmain ()
- •Int main ()
- •Int main ()
- •Динамически растущие массивы (векторы)
- •Int main ()
- •Struct IntegerVector
- •Int * m_pData;
- •Int m_nUsed;
- •Int m_nAllocated;
- •Void IntegerVectorDestroy ( IntegerVector & _vector )
- •#Ifndef_integer_vector_hpp_
- •#Include"integer_vector.Hpp"
- •Void IntegerVectorRead ( IntegerVector & _vector, std::istream & _stream );
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[]):


Очевидно, такое поведение является неприемлемым. Наивное решение состоит в увеличении размера выделяемой памяти. Но на любое подобное допущение всегда найдется набор входных данных, который разрушит программу. Еще одна наивная мысль - выделить огромное количество ячеек, скажем миллион, что скорее всего хватит для практически любых случаев, которые реально могут встретиться. Огромный недостаток этой очередной идеи состоит в крайне нерациональном расходовании ресурсов памяти. Несколько подобных приемов - и никакого объема оперативной памяти на компьютере не хватит.
В результате данного рассуждения остается лишь прийти к следующим выводам:
Программа должна обрабатывать количество данных, которое в нее вводит пользователь.
Программа не должна завершаться фатально ни при каком вводе.
Программа должна рационально расходовать память, выделяя необходимый ей минимум.
Каким же образом можно удовлетворить вышеуказанным требованиям к реализации?
Динамически растущие массивы (векторы)
Применим простейшую классическую структуру данных - динамически растущий массив. Во многих языках программирования эту структуру также называют вектором.
Основная идея такого массива состоит в резервировании некоторого небольшого объема памяти для данных и последующем автоматическом расширении как только текущий объем полностью заполняется. Для организации такой структуры необходимо ввести 3 переменные:
адрес начала текущего блока памяти для хранения (pData);
число выделенных ячеек в текущем блоке памяти (nAllocated);
число фактически занятых полезными данными ячеек в данный момент (nUsed).
Начальный выделяемый объем может быть произволен, разумной начальной настройкой является 8-10 ячеек. В начале часть выделенных ячеек использоваться не будет. Ячейки будут заполняться данными по мере ввода:

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

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