Работа с памятью (обобщение)

Данная статья - скорее всего обобщение того, что упоминалось в прошлых статьях относительно памяти, плюс к этому кое-что будет новым.
Память для переменных, структур и прочего выделяется автоматически (заложено в возможностях компилятора), и вручную. Первый способ не требует вмешательства программиста для очистки памяти по окончанию использования. Второй способ этого требует. Однако второй способ дает более гибкие возможности (вспоминаем связанные списки).
С первым способом мы соприкасались постоянно, когда объявляли обычные переменные, структуры, массивы. Компилятор сам автоматом выделял для их память, и освобождал когда это требовалось. Второго способа мы лишь чуточку коснулись. Давайте в этой статье его рассмотрим более подробно.

 

Выделение памяти с помощью Allocate

Функция Allocate позволяет выделять нужный размер памяти, при том надо помнить что по окончанию необходимо ее освободить с помощью функции Deallocate. Я бы вам посоветовал при написании кода, сразу по написании функции Allocate ниже писать Deallocate для того, чтобы не забыть после.
Функция Allocate имеет в себе всего один параметр: размер памяти в байтах.

 Например:

'автоматическое выделение памяти
Dim As Integer AAAA

'ручное выделение памяти
Dim As Integer Ptr BBBB = Allocate(sizeof(Integer))
' ------работа с переменной-------
'очистка выделенной памяти
Deallocate(BBBB)

Ручное выделение памяти тем хорошо, что позволяет выделять одним разом нужное нестандартное кол-во памяти (под стандартными имелось ввиду: byte, integer и др.)

 

Выделение памяти с помощью СAllocate

Функция Callocate так же выделяет память, но в отличии от Allocate заполняет выделенную память нулями. Конечно же из-за этой способности скорость ее чуть ниже, но заполнение нулями часто бывает нужным. По окончании использования памяти, ее так же нужно освобождать с помощью Deallocate. У функции Callocate два параметра: первый кол-во участков памяти для выделения, второй размер в байтах(по умолчанию 1) для одного участка. По сути можно использовать как и Allocate выделяя заранее определенный размер, например так:

Dim As Integer Ptr BBBB = СAllocate(sizeof(Integer))
' ------работа с переменной-------
'очистка выделенной памяти
Deallocate(BBBB)

Или так:

Dim As Integer Ptr BBBB = СAllocate(35, Sizeof(Integer))
 ' ------работа с переменной-------
 'очистка выделенной памяти
Deallocate(BBBB)

Во втором примере выделяется памяти 35*4=140 байт (на windows 32) или 35*8=280 байт (на windows 64), поскольку оператор Sizeof подсчитывает размер в байтах типа integer. А тип Integer, как нам известно, равен 4 или 8 байт в зависимости от разрядности операционной системы.

 

Переопределение памяти с помощью ReAllocate

Функция Reallocate позволяет изменить кол-во выделенной памяти, при этом сохраняя ее содержимое.

Dim As Byte Ptr a = Callocate(5),b
Poke a+2,12
b = Reallocate(a,10)
? Peek(b+2)
Deallocate(b)
Sleep


В примере объявляем два указателя на область памяти, при том для одного из них выделяем память. Далее с помощью Poke записываем значение по адресу a+2 . Затем переопределяем память и присваиваем ее указателю b , при том память по указателю a автоматически освобождается. Далее считываем то, что находится по адресу b+2 по сути тоже смещение, что и a+2 И освобождаем память по указателю b 

 

Операторы Poke и Peek

Мы уже в одной из статей рассматривали вкратце эти операторы, но давайте чуть подробнее. Оператор Poke нужен для занесения значения по указанному адресу. Оператор Peek для получения значения из указанного адреса. А теперь рассмотрим их параметры:

Poke  тип , адрес или смещение по адресу,  значение

значение = Peek  (тип , адрес или смещение по адресу )

Обратите внимание, оператор Poke пишется без скобок. Параметр тип является необязательным. Если его не использовать, то по умолчанию тип равен Ubyte. 
Смещение по адресу-это как раз и есть то, что мы использовали a+2. То есть выделяя память, нам для отсчета дается адрес. Он имеет нулевое смещение a+0. Таким образом, когда выделили 5 байт, его конечное смещение будет равно a+4  Массивы построены как раз по этому принципу.

 

Оператор [ ]

Если уж я заговорил о смещении, то нельзя пропустить более удобное обращение к памяти с помощью оператора [ ]  Используя этот оператор, обращение ведется почти как у массивов, разница лишь в скобках (у массивов круглые)

Dim As Byte Ptr a= Callocate(5)
a[2]=12
? a[2]
Deallocate(a)
Sleep


Удобно? Мне кажется да. Объяснять тут по моему просто нечего, все и так прозрачно.

 

Оператор Clear

Данная функция заполняет определенным числом выделенную область. В примере ниже заполняется нулями вся выделенная область , но можно заполнять любую ее часть и не только нулями. Для того чтобы заполнить другим числом, достаточно во втором параметре функции Clear передать это число. Первый параметр этой функции смещение по адресу, третий кол-во в байтах для заполнения.

Dim As Byte Ptr a= Allocate(5)
Clear(a[0],,4)
? a[0]
Deallocate(a)
Sleep


Мы уже немало говорили об операторах ->  * и @ поэтому останавливаться на них не буду, но думаю стоит остановиться на операторах New и Delete

 

Операторы New и Delete

Оператор New выделяет память, одновременно подсчитывая размер типа для которого выделяется память. Оператор Delete ее освобождает. NEW имеет два параметра, записываемых чуть с непривычным синтаксисом. Первый - это тип , по которому оператор выделит память. Второй параметр - кол-во для выделения по данному типу.

Dim As Byte Ptr a= New Byte [5]
a[2]=10
? a[2]
Delete [] a
Sleep


Если во втором параметре оператора New не указывать кол-во, то по умолчанию будет единица.
Вспоминается мне, в одной из статей я хотел показать значимость деструктора. Как раз с помощью оператора Delete мы это и сделаем . Для примера нам подойдет код связанного списка, только чуть дополним его:

'---------Класс связанного списка------------
'--------------------------------------------

'подкласс B
Type B
    next_ As B Ptr =0
    prev_ As B Ptr =0
    value As Byte =0
End Type
'подкласс A (главный)
Type A
    First_    As B Ptr =0
    last_     As B Ptr =0
    Declare Sub Add_last(As Byte)
    Declare Sub All()
    Declare Destructor()
End Type

' метод добавления пункта в конец
Sub A.Add_last(value_ As Byte)
    Dim temp As B Ptr = New B
    temp->value=value_
    If first_=0 Then
        first_=temp
    Else
        last_->next_=temp
        temp->prev_=last_
    Endif
    last_    = temp
End Sub
' метод вывода всех значений
Sub A.ALL()
    Dim temp As B Ptr =first_
    Do
        ? temp->value
        temp=temp->next_
    Loop While temp<>0
End Sub
' Удаление листа
Destructor A()
Dim temp As B Ptr
Dim del As B Ptr =first_
Do
    temp=del->next_
    Delete del
    del=temp
Loop While temp<>0
End Destructor

'----------конец класса-------------
'-----------------------------------
'------использование класса----------

Dim list As A Ptr = New A
list->Add_last(10)
list->Add_last(20)
list->Add_last(30)

list->All

Delete list
Sleep


Вы можете еще раз обратить внимание на оператор NEW . В этом примере New подсчитывает размер занимаемой структуры A и выделяет память нужную для работы. Оператор Delete при удалении объекта листа, сначала запускает деструктор, удаляет все привязанные объекты, потом уже удаляет сам главный объект листа. И конечно же при всем этом освобождается память.

 

Оператор FRE

 Этот оператор показывает кол-во незанятой памяти.

Dim mem As Integer = Fre
Print "Free memory:"
Print
Print mem; " bytes"
Print mem  \ 1024; " kilobytes"
Print mem  \ (1024 * 1024); " megabytes"
Sleep


Данный пример взят из справки. В первой строчке переменной mem присваивается кол-во свободной памяти в байтах. Далее выводится в консоль кол-во памяти в байтах , килобайтах, мегабайтах.

 

Копирование и перемещение памяти

У языка FreeBasic как таковой нет простой встроенной возможности копировать и перемещать выделенные нестандартные области памяти. Я не беру в расчет копирование в цикле. Однако этого легко можно добиться с помощью API функций MoveMemory  и  CopyMemory.  Функции используются совершенно одинаково, поэтому покажу на одном примере.

#INCLUDE "windows.bi"
Dim As Byte Ptr a = Allocate(10), b = Allocate(10)
Clear(a[0],20,10)
CopyMemory(b,a,10)
? b[2]
Sleep
Deallocate(a):Deallocate(b)


Для того, чтобы использовать эти функции нужно подключить библиотеку, в которой они находятся с помощью заголовочного файла "windows.bi"

У обоих функций одинаковые параметры:

  1. буфер куда копировать или перемещать
  2. буфер откуда копировать или перемещать
  3. размер для копирования или перемещения в байтах  

В примере мы выделили память для двух блоков памяти. Далее с помощью Clear заполнили один из буферов значениями 20. Потом скопировали содержимое буфера a в буфер b. Затем вывели значение одной из ячеек буфера b. И освободили выделенную память.

Пожалуй это все, всего доброго!

содержание | назад | вперед