Указатели

        Обычно указатели хранят адрес в памяти того или иного объекта (переменной, структуры, процедуры и т. д.), т. е. указывают на этот объект. Не скажу что работать с указателями приходится очень часто, но иногда возникает такая необходимость. Для хранения адреса памяти существует специальный тип переменной. В начале такой переменной находится символ * (звёздочка). То есть это *pointer переменная-указатель на область памяти.
PureBasic позволяет получать адреса, переменных, массивов, структур, связанных списков, процедур и меток.
Адрес метки можно получить подставив перед её именем символ ?,  адреса остальных объектов можно получить поставив перед их именем символ @.
Следующий пример показывает как можно получить адрес переменной и работать с ней как с памятью.

var=100 ; Запись числа 100 в переменную
*pointer=@var ; Поучение адреса переменной
Debug PeekC(*pointer) ; Чтение данных из переменной зная её адрес в памяти



В первой строке происходит запись числа в переменную. Узнаём адрес в памяти где хранятся данные этой переменной, поставив перед её именем "собаку" - символ @. После чего, с помощью функции PeekC происходит чтение данных из памяти, т. е. из переменной var.

Помните, в статье Работа с памятью был приведён пример программы, которая считываем текст из файла и отображает его в текстовом редакторе. Там данные сначала копировались из файла в память, а затем из памяти в строковую переменную. Этот пример можно упростить, исключив работу с памятью, точнее можно использовать строковую переменную как память. Тогда данные будут копироваться из файла сразу в переменную.
Пример будет выглядеть так:

File.s=OpenFileRequester("","","Текстовые файлы (txt)|*.txt",0)
If File<>""
  If ReadFile(0, File) ; Открытие файла
    Size=Lof(0) ; Определение размера файла в байтах
    Text.s=Space(Size) ; Заполнение строковой переменной пробелами
    ReadData(0, @Text, Size) ; Копирование данных из файла в переменную
    CloseFile(0) ; Закрытие файла
  Else
    MessageRequester("", "Не удалось открыть файл")
    End ; Завершение работы программы
  EndIf
EndIf
; Открываем окно
OpenWindow(1,0,0,400,400,"Работа с памятью", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
CreateGadgetList(WindowID(1))
EditorGadget(1,2,2,396,396) ; Создаём текстовый редактор
SetGadgetText(1,Text) ; Помещаем текст из строковой переменной в редактор

Repeat ; Главный цикл программы
 Event=WaitWindowEvent() ; Получаем идентификатор события в программе
Until Event=#PB_Event_CloseWindow  ; Прерываем цикл если нужно закрыть окно



Перед чтением теста из файла, в строковую переменную Text, с помощью функции Space записывается число пробелов, равное размеру файла. Это нужно чтобы зарезервировать в переменной требуемое число байт. Далее, при помощи функции ReadData производится копирование данных из файла в переменную. Функция как и положено получает указатель на память, только это память, занимаемая переменной.  

С помощью указателей можно возвращать из процедуры более одного результата и при этом не использовать глобальные переменные. Следующий пример демонстрирует это.


Procedure Test(*var1, *var2)
 PokeL(*var1, 12345678) ; Запись числа в память
 PokeS(*var2, "Строка текста") ; Запись текста в память
EndProcedure

var.l=0
Text.s=Space(100) ; Резервируем данные в строковой переменной, путём записи 100 пробелов
Test(@var, @Text) ; Вызываем процедуру и передаём ей указатели на переменные var и Text
; Отображаем в отладочном окне данные из переменных.
Debug var
Debug Text


При инициализации переменных, в var записывается число 0, а в строковую переменную Text, записывается 100 пробелов, т. е. резервируется память. Далее вызывается процедура Test, которой передаются указатели на эти переменные. В процедуре производится запись данных в эти переменные, причём с ними производится работа как с памятью. И в конце программы, с помощью оператора Debug, в отладочном окне отображается данные из этих переменных.

Но гораздо удобнее работать со структурами, т. к. не нужно использовать функции доступа к памяти.

Structure Proba
  x.l
  y.l
  Text.s
EndStructure

test.Proba ; Объявление структуры

Procedure Test(*var.Proba)
 *var\x=1
 *var\y=2
 *var\Text="Текст"
EndProcedure

Test(@test) ; Вызываем процедуру и передаём ей указатель на структуру
; Отображаем в отладочном окне данные из структуры
Debug test\x
Debug test\y
Debug
test\Text


В следующем примере показано, как можно вызывать процедуру зная её адрес в памяти.

Procedure Message(Title.s, Message.s)
 MessageRequester(Title, Message)
EndProcedure
; Узнаём адрес процедуры и помещаем его в переменную-указатель
*ponter=@Message()
; Вызываем процедуру зная её адрес в памяти
CallFunctionFast(*ponter, "Заголовок", "Текст")




Возможность получать адрес метки, позволяет сохранять различные данные в теле исполняемого файла используя так называемую ДатаСекцию. Это позволяет хранить, скажем один или несколько файлов, требуемых для работы программы. Чаще всего это значки, различные рисунки, музыка и т. д. Можно даже хранить динамическую библиотекиу подпрограмм (исполняемый файл с расширением DLL), которую можно запускать прямо из памяти.

Следующий пример показывает как можно поместить значок в исполняемый файл и использовать его в программе.

; Открываем окно
OpenWindow(1,0,0,80,80,"",#PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
CreateGadgetList(WindowID(1)) ; Создаём новый список гаджетов
 
  CatchImage(1,?Metka1, ?Metka2-?Metka1) ; Загружаем рисунок из памяти
  ImageGadget(2,40,20,32,32, ImageID(1) ) ; Отображаем его
 
Repeat ; Начало главного цикла Repeat-Until
 Event=WaitWindowEvent() ; Получаем текущий идентификатор события
; Прерываем цикл при попытке закрыть окно (щелчёк по крестику в заголовке окна)
Until Event=#PB_Event_CloseWindow 
End ; Завершаем работу программы

DataSection
 Metka1:
  IncludeBinary "Значок.ico"
 Metka2:
EndDataSection



На этапе компиляции, с помощью оператора IncludeBinary в исполняемый файл записывается файл с именем Значок.ico, находящийся в одной папке с исходным текстом. Метки перед и после этого оператора позволяют узнать место в памяти, где хранится файл, в нашем случае значок, который в функции CatchImage загружается, а затем отображается в окне. Первый аргумент этой функции - идентификатор создаваемого рисунка, второй аргумент - адрес памяти начала рисунка, который получаем с помощью символа ? перед именем метки. Третий аргумент - размер памяти в байтах, занимаемый рисунком. Чтобы его получить, просто отнимаем адрес второй метки от адреса первой метки.

Скомпилированную программу и её исходный текст можно скачать здесь.


Учебник                Главная
Сайт создан в системе uCoz