Статические и динамические массивы
Статическим массивом называют набор данных, для хранения которого перед началом функционирования программы выделяется фиксированное место в памяти, освобождаемое после завершения работы программы.
QBasic трактует это понятие несколько шире, сохраняя значения статических массивов и после останова или полного завершения работы программы. Не выходя из интегрированной среды, можно посмотреть значения элементов статических массивов. В отличие от этого, место для хранения динамических массивов выделяется и освобождается в процессе выполнения программы. В одних случаях эти операции осуществляются системой автоматически. Например, когда отводится память для хранения локальных массивов в процедурах и функциях. В других случаях пользователю предоставляется возможность запросить участок памяти нужного размера и в дальнейшем — освободить его. Только таким способом в программах на Си и Паскале можно завести массив переменного размера.
В системе QBasic по умолчанию массивы с конкретными числовыми границами считаются статическими, а с переменными границами —динамическими. Однако, если первой строкой программы задана метакоманда вида КЕМ $STATIC или REM $DYNAMIC, то все массивы в программе будут заводиться либо как статические, либо как динамические. По оператору ERASE v1,v2, ... статические массивы "чистятся", а динамические массивы освобождают занимаемую память. Чистка массивов подразумевает засылку нулевых значений во все элементы числовых массивов и засылку "пустых" (нулевых) строк в элементы массива символьного типа. Оператор REDIM позволяет переопределить размеры ранее объявленных динамических массивов, не изменяя количество приписанных им индексов. Одновременно с этим происходит чистка переобъявляемых массивов.
В Си для запроса и освобождения памяти используются следующие системные функции:
q=(тип_q *)calloc(n_el,s_el); //запрос памяти с очисткой;
q=(тип q *)farcalloc(n_el,s_el); //запрос памяти с очисткой;
q=(тип_q *)malloc(n_byte); //запрос памяти в ближней "куче"
q=(тип_q *)farmalloc(n_byte); //запрос памяти в дальней "куче"
q new=realloc(q_old,n_byte); //изменение размера блока
q_new=farrealloc(q_old,n_byte); //изменение размера блока
free(q); //освобождение памяти
farfree(q); //освобождение памяти
В приведенных выше обращениях q обозначает указатель на тип данных элементов массива, заменяющий имя массива. Параметры n_el и s_el задают соответственно количество элементов в массиве и длину каждого элемента в байтах. Параметр n_byte определяет количество запрашиваемых байтов.
Максимальный размер сегмента памяти, предоставляемого в ближней "куче", равен 65 521 байт. Добавка far означает, что программа использует дальние указатели типа far или huge, которые позволяют адресоваться к дальней "куче" и использовать сегменты размером более 64 Кбайт. Любая функция выделения памяти возвращает начальный адрес или "нулевой" указатель (NULL) в случае отсутствия свободной памяти запрашиваемого размера. Для того чтобы нормально работать с предоставленным фрагментом памяти, возвращаемый адрес обязательно должен быть приведен к типу указателя q.
Функция realloc (farreallioc) позволяет перераспределить ранее выделенную память. При этом новый размер массива может быть как меньше предыдущего, так и больше. Если система выделит память в новом месте, то все предыдущие значения, к которым программа обращалась по указателю q_old, будут переписаны на новое место автоматически.
В новых версиях Borland C++ появились две более удобные процедуры для запроса и освобождения памяти, не нуждающиеся в дополнительном указании о приведении типа возвращаемого адреса:
q=new тип[n_е1]; //запрос памяти под массив из n_e1 элементов;
q=new тип; //запрос памяти под скалярную переменную;
delete q[n_e1]; //освобождение памяти, занятой массивом;
delete q; //освобождение памяти, занятой массивом или
//скалярной переменной;.
Для формирования в Паскале динамического массива с элементами определенного типа с таким массивом надо связать соответствующий указатель и обратиться к процедуре New:
type
mas=array [1..200] of integer;
point=^mas; var
p:point;
j:integer;
..................
begin New(p);
for j :=1 to 200 do
readln(p^[j]);
...................
Запрошенная таким образом память освобождается процедурой Dispose (р). Для запроса и освобождения памяти под разнородные данные в Паскале можно использовать процедуры GetMem(q,n_byte) и FreeMem(q,n_byte). Указатель я, которому присваивается начальный адрес выделенного сегмента, в данном случае представлен нетипизированным указателем (q:pointer;).
Как в Си, так и в Паскале, процедура освобождения памяти не чистит указатель, "смотревший" на начало возвращаемого сегмента. Запись по такому указателю после возврата памяти приводит к трудно обнаруживаемым ошибкам. Поэтому к правилам "хорошего тона" в программировании относится и сброс указателей после возврата динамически запрашивавшейся памяти:
q=NULL; //так это делается в Си
p:=nil; //так это делается на Паскале
Не менее хорошее правило заключается и в проверке, выделена ли запрашиваемая память после обращения к соответствующей процедуре. Например, в Си, не контролирующем запись по нулевому адресу, после стирания нескольких первых элементов несуществующего массива происходит зависание операционной системы.