Обычно указатели хранят адрес в
памяти того или иного объекта (переменной, структуры, процедуры и т.
д.), т. е. указывают на этот объект. Не скажу
что работать с указателями приходится очень часто, но иногда возникает
такая необходимость. Для хранения адреса памяти существует специальный
тип переменной. В начале такой переменной находится символ
* (звёздочка).
То есть это
*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
загружается, а затем отображается в окне. Первый аргумент этой функции
- идентификатор создаваемого рисунка, второй аргумент - адрес памяти
начала рисунка, который получаем с помощью символа
?
перед именем метки. Третий аргумент - размер памяти в байтах,
занимаемый рисунком. Чтобы его получить, просто отнимаем адрес второй
метки от адреса первой метки.
Скомпилированную программу и её исходный текст
можно скачать здесь.