Память под динамические переменные выделяется из свободной памяти компьютера (за пределами загруженного в нее exe-файла) по запросу программы. Размер выделяемой памяти может меняться.
Рис. 13.2. Размещение динамических переменных в памяти.
Чтобы наша программа могла записывать и считывать данные из динамической переменной, нужно, чтобы операционная система пометила некоторый участок памяти как занятый и не пускала в него другие программы. Кроме того, в программе следует предусмотреть обычную статическую переменную-указатель, в которой будет храниться адрес выделенного куска памяти. Иначе говоря, динамическая переменная состоит из двух частей: ее значение лежит в оперативной памяти компьютера вне exe-файла, а обращаться к этому значению придется через статическую переменную-указатель, находящуюся внутри exe-файла. В статической переменной – указателе хранятся адрес и размер участка памяти, в котором содержится значение переменной – указателя, а в ряде случаев - и тип данных, которые можно размещать в этой динамической переменной (Рис. 9.3).
Рис. 13.3. Указатели и динамические переменные.
В тексте программы создание динамической переменной выглядит следующим образом:
TYPE TA=ARRAY[1..10] OF REAL; VAR p:^BYTE; { указатель на байт } p1:^TA; { указатель на массив }
Здесь p и p1 – статические переменные, в которых можно хранить адрес байта и массива типа TA соответственно. Знак "^" соответствует слову "указатель". Переменная p не будет занимать 1 байт – это указатель, в котором хранится четырехбайтный адрес в памяти, да еще и тип данных.
Важное замечание: оператор VAR динамическую память НЕ ВЫДЕЛЯЕТ. Он просто создает статические переменные-указатели. Области же памяти для хранения значений этих переменных создаются уже в момент работы программы.
Следующие функции предназначены для резервирования в оперативной памяти места под динамические переменные:
NEW(указатель) – выделяет кусок в динамической памяти и записывает его адрес в переменную – указатель
DISPOSE(указатель) – освобождает ранее захваченный кусок памяти и присваивает указателю значение NIL. Указатель, значение которого равно NIL:, указывает "в никуда", и с ним не связано никакой динамической памяти.
Самое главное: сколько памяти захватили, столько и вернули. Число команд NEW должно строго равняться числу команд DISPOSE. Например:
VAR p:^WORD;
BEGIN { выделили 2 байта и записали их адрес в р} New(p); … { вернули 2 байта и записали в p NIL } Dispose(p) END.
А как работать со значением динамической переменной? Очень просто – если после имени переменной-указателя поставить галочку "^", то это будет означать обращение не к переменной-указателю, а к значению, на которое эта переменная указывает. Иными словами, если p – указатель на область памяти; то p^ - значение, хранящееся в этой области. Рассмотрим пример:
VAR p:^WORD;
BEGIN New(p); p^:=200: Label1.Caption:=FloatToStr(p^*2); Dispose(p) END.
Может возникнуть резонный вопрос: если с каждой динамической переменной связана статическая переменная-указатель, то стоит ли весь сыр-бор заводить? Да, стоит. Во-первых, размер динамической переменной можно поменять "на ходу". Во-вторых, указатели на массив из десяти и миллиона элементов занимают совершенно одинаковый объем.