
- •Ста: Лекция №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 ()
{
inta = 5;// Точка 1
f( 3 );
// Точка 3
}
В отличие от сегмента кода и сегмента данных, точный необходимый размер для стека вычислить во время компиляции не представляется возможным, т.к. функции могут вызывать друг друга с очень большим и непредсказуемым уровнем вложенности. Соответственно, выделяется некоторый конечный достаточно большой для общего назначения размер. Этот размер зависит от используемого компилятора и может быть настроен в случае необходимости. Программы в среде Visual Studio по умолчанию получают стек размером 1 мегабайт. Достичь данного предела не так уж сложно, достаточно объявить внутри функции слишком большой по размеру массив:
Int main ()
{
Char buf[ 2000000 ];
buf[ 0 ] = 'a';
}
Запуск такой программы приведет к ошибке переполнения стека:
Из этого вытекает, что стек не предназначен для хранения больших объемов данных, а должен использоваться лишь для промежуточных переменных и аргументов функций.
Переполнение стека также может случиться из-за слишком глубокого уровня вложенности вызовов функций. Это легко продемонстрировать на приведенной ниже некорректной рекурсивной функции. Хотя функция и не содержит ни одного локальной переменной и ни одного аргумента, объем сохраняемой служебной информации через некоторое время превысит ограничение на размер стека:
int f ()
{
Return f();// бесконечно долго вызываем сами себя // вызываем переполнение стека через время
}
Int main ()
{
Return f();
}
Неаккуратные манипуляции с адресами памяти в области стека могут не только непроизвольно затереть значения соседствующих переменных, но и задеть служебную информацию, размещаемую в стеке. Ошибиться очень легко, достаточно при обращении к массиву выйти за допустимую границу индексов. При неудачном попадании случайно модифицируемых ячеек представляется возможным затереть адрес инструкции для возврата из функции, что полностью разрушит стек и фатально завершит программу с трудно уловимым местом ошибки.
Вся остальная наибольшая область памяти называется кучей (heap), или динамической памятью, и распределяется программистом вручную при помощи операторовnew. В отличие от других областей памяти, освобождение блоков в динамической памяти также нужно делать вручную, используя операторыdelete. Например, следующая программа выделяет блок памяти из 10 миллионов целых чисел, а затем освобождает его:
Int main ()
{
int * p =new int[ 10000000 ];// Выделяем память
Delete[]p;// Освобождаем память
}
Если при помощи new выделялся массив, то освобождать его нужно при помощи оператораdelete[]. Если выделялся одиночный объект - его освобождают через операторdelete. В ряде сред разработки нарушение данного правила приводит к фатальному завершению программы (к сожалению, Visual Studio о такой ошибке никак не сигнализирует).
Наиболее важное преимущество динамической памяти - возможность определения объема выделения во время выполнения программы. Выделяя массивы в глобальной области либо на стеке необходимо указывать их конкретные исчисляемые во время компиляции размеры. При выделении же массивов в динамической памяти, их размер может определяться значением любого выражения с переменными, зависящего от текущего состояния программы, внешних данных от пользователя и других факторов.
#include<iostream>