PureBasic

Указатели и доступ к памяти

Краткое описание функций:
Указатели '*' Указатели и объём памяти Указатели и структуры
Указатели и Массивы Указатели и символьные строки Арифметика указателей
Адреса переменных: '@' Адреса символьных строк Адреса процедур: '@'
Адреса меток: '?'

Указатели '*'

Указатели - это переменные специального (указательного) типа, которые хранят адрес объекта в памяти. По этому адресу может находиться Переменная, Массив, Связный список, Хеш-карта, Структура, Процедура или Метка. В начале имени такой переменной находится символ '*' (звёздочка). То есть, *pointer - это переменная содержащая адрес памяти, который часто связан с Структурой.

Пример

*MyScreen.Screen = OpenScreen(0, 320, 200, 8, 0)
mouseX = *MyScreen\MouseX  ; принятая экранная структура содержит поле MouseX

Есть только три правильных метода, чтобы установить значение указателя:
- Получить результат функцией (как показано в примере выше)
- Скопировать значение из другого указателя
- Найти адрес переменной, процедуры или метки (как показано ниже)

Адрес метки можно получить подставив перед её именем символ '?', адреса остальных объектов можно получить поставив перед их именем символ '@'.

Примечание: в отличие от C/C++, в PureBasic '*' неотъемлемая часть имени объекта. Поэтому *ptr и ptr - две различные переменные. ptr является переменной сохраняющей значения (то есть обычной переменной), *ptr является переменной ссылочного типа, сохраняющей адрес, то есть указателем.



Указатели и объём памяти

Так как указатели принимают как значение только адрес, размер в оперативной памяти будет таким, чтобы позволить сохранить абсолютный адрес микропроцессора:
- На 32-битных процессорах, адресное пространство ограничено 32 битами, поэтому указатель будет занимать в памяти 32 бита (4 байта, как 'long')
- На 64-битных процессорах, адресное пространство ограничено 64 битами, поэтому указатель будет занимать в памяти 64 бита (8 байт, как 'quad').

Как следствие тип указателя зависит от адресного пространства центрального процессора, ('long' на центральном процессоре на 32 бита и 'quad' на центральном процессоре на 64 бита ).
Это значит, что присвоение собственного типа к указателю (*Pointer.l, *Pointer.b...) не имеет смысла.

Примечание:
- Каждый раз, когда адрес памяти должен быть сохранен в переменной, это должно быть сделано через указатель. Это гарантирует неприкосновенность адреса во время компиляции независимо от способа адресации процессора.
- PureBasic x86 не создает 64-разрядные исполняемые файлы. Для программ созданных с помощью PureBasic x86, система предоставляет адресацию только для 32-разрядных указателей.


Указатели и структуры

Путем связывания структуры и указателя (например, *MyPointer.Point), можно получать доступ к любому адресу памяти структуры (с оператором '\').

Пример: Указатели и Структуры

Define Point1.Point, Point2.Point ; 2 переменных типа 'Point' (предопределенный тип в PureBasic)
*CurrentPoint.Point = @Point1      ; Объявление указателя связанного со структурой, и его инициализация с адресом Point1
*CurrentPoint\x = 10              ; Присваиваем значение 10 переменной Point1\x через указатель
*CurrentPoint.Point = @Point2      ; Переходим к адресу Point2
*CurrentPoint\x = 20              ; Присваиваем значение 20 переменной Point2\x через указатель
Debug Point1\x                      ; Отображаем переменную Point1\x
Debug Point2\x                      ; Отображаем переменную Point2\x



Указатели и Массивы
Присоединение массива к указателю обеспечивает доступ к содержимому памяти каждой ячейки через ее адрес.

Пример: Указатели и массив

Define Point1.Point, Point2.Point ; 2 переменных типа 'Point' (предопределенный тип в PureBasic)
Dim *Points.Point(1)              ; Массив из 2 указателей с типом 'Point'
*Points(0) = @Point1              ; Первый указатель сохраняет адрес переменной Point1
*Points(1) = @Point2              ; Второй указатель сохраняет адрес переменной point2
*Points(0)\x = 10                  ; Присваиваем значение 10 переменной Point1\x через указатель
*Points(1)\x = 20                  ; Присваиваем значение 20 переменной Point2\x через указатель
Debug Point1\x                      ; Отображаем переменную Point1\x
Debug Point2\x                      ; Отображаем переменную Point2\x

Пример: Указатели и Связный список

Define Point1.Point, Point2.Point ; 2 переменных типа 'Point' (предопределенный тип в PureBasic)
NewList *Points.Point()              ; Создаём Связный список с типом 'Point' под элементы-указатели
AddElement(*Points())              ; Добавляем элемент, он становится текущим.
*Points()  = @Point1              ; Текущий элемент-указатель сохраняет адрес переменной Point1\x
AddElement(*Points())              ; Добавляем ещё элемент, он становится текущим.
*Points()  = @Point2              ; Текущий элемент-указатель сохраняет адрес переменной Point2\x
FirstElement(*Points())              ; Делаем текущим первый элемент
*Points()\x = 10                  ; Присваиваем значение 10 переменной Point1\x через текущий элемент-указатель
NextElement(*Points())              ; Делаем текущим следующий элемент
*Points()\x = 20                  ; Присваиваем значение 20 переменной Point2\x через текущий элемент-указатель
Debug Point1\x                      ; Отображаем переменную Point1\x
Debug Point2\x                      ; Отображаем переменную Point2\x

Пример: Указатели и Хеш-Карта

Define Point1.Point, Point2.Point ; 2 переменных типа 'Point' (предопределенный тип в PureBasic)
NewMap *Points.Point()              ; Создаём Хеш-карту с типом 'Point' под элементы-указатели
AddMapElement(*Points(),"1")      ; Добавляем элемент, он становится текущим. Ключ можно использовать как индекс.
*Points()  = @Point1              ; Текущий элемент-указатель сохраняет адрес переменной Point1
AddMapElement(*Points(),"2")      ; Добавляем ещё элемент, он становится текущим.
*Points()  = @Point2              ; Текущий элемент-указатель сохраняет адрес переменной Point2
ResetMap(*Points())                  ; Устанавливаем внутренний указатель Хеш-карты на "элемент перед первым"
NextMapElement(*Points())          ; Делаем текущим следующий элемент (в данном случае первый)
*Points()\x = 10                  ; Присваиваем значение 10 переменной Point1\x через текущий элемент-указатель
NextMapElement(*Points())          ; Делаем текущим следующий элемент
*Points()\x = 20                  ; Присваиваем значение 20 переменной Point2\x через текущий элемент-указатель
Debug Point1\x                      ; Отображаем переменную Point1\x
Debug Point2\x                      ; Отображаем переменную Point2\x

Указатели позволяют производить перемещение, чтение и запись в памяти. Кроме того они позволяют программисту обратиться к большому объёму данных, без дополнительных затрат на копирование данных. Копирование указателя намного быстрее, так как он занимает всего 4 или 8 байт.

Указатели также доступы в структурах, для получения дополнительной информации смотрите главу структур.


Указатели и символьные строки

У всех переменных есть постоянный размер в памяти (2 байта для Word, 4 байта для Long, и т.д..), за исключением строк, так как их длина может измениться. По этой причине, ими управляют по-другому.
Таким образом, поля структуры, ссылающиеся на строку, хранят её адрес, а не саму строку: такое поле структуры - это указатель на строку.

Пример Указатели и символьные строки

Text$ = "Hello"
*Text = @Text$            ; Сохраняет адрес строки в памяти в переменной *Text
*Pointer.String = @*Text  ; *Pointer - это указатель на *Text
Debug *Pointer\s          ; Выводит на экран строку, находящуюся по адресу, сохраненному в *Pointer (т.е. @Text$)



Арифметика Указателей

Арифметические операции с указателями, возможны с помощью команды SizeOf().

Пример Арифметика Указателей

Dim Array.Point(1) ; Массив точек

*Pointer.Point = @Array() ; Сохраняет адрес массива
*Pointer\x = 10              ; Изменяет значение первого элемента массива
*Pointer\y = 15
*Pointer + SizeOf(Point) ; Перейти к следующему элементу массива
*Pointer\x = 7              ; Изменяет значение второго элемента массива
*Pointer\y = 9

; Отображение результата
For i = 0 To 1
    Debug Array(i)\x
    Debug Array(i)\y
Next i



Адреса переменных '@'

Чтобы получить адрес переменной в вашем коде, используйте символ (@). Общей причиной для использования этого, является передача переменной структурированного типа в процедуру. Вы должны передать указатель на эту переменную, так как вы не можете передать структурированную переменную напрямую.

Пример Указатели и переменные

Structure astruct
    a.w
    b.l
    c.w
EndStructure

Procedure SetB(*myptr.astruct)
    *myptr\b = 69
EndProcedure

Define.astruct myvar
SetB(@myvar)
Debug myvar\b


Адреса символьных строк

Чтобы получить адрес литеральной строки, вы можете использовать символ (@) перед ней. Строковые константы также поддерживаются.

Пример

*String = @"Test"
Debug PeekC(*String) ; Будет отображаться 84, что является значением 'T'



Адреса процедур '@'

Для опытных программистов. Наиболее распространенной причиной получения адреса процедуры является работа с ОС на низком уровне. Некоторые операционные системы позволяют вам указывать функции обратного вызова или функции обработчика прерываний (для некоторых операций), которые вызываются операционной системой и позволяют программисту расширить возможности ОС. Адрес процедуры находится так же, как и переменной, с помощью добавления к имени символа '@'.

Пример Указатели и процедуры

Procedure WindowCB(WindowID.i, Message.i, wParam.i, lParam.i)
    ; Здесь будет выполняться обработка вашей процедуры обратного вызова
EndProcedure

; Специальный обратный вызов для ОС Windows, позволяющий обрабатывать события окна
SetWindowCallback(@WindowCB() )

Пример Указатели и процедуры (простой вариант)

Procedure x1() ; Создание процедуры x1()
    Debug #PB_Compiler_Procedure ; Здесь любые нужные Вам действия.
EndProcedure

CallFunctionFast(@x1()) ; Обращение к процедуре x1() по адресу


Адреса меток '?'

Также может оказаться полезным найти адреса меток в Вашем коде. Это может пригодиться для обращения к коду или данным, которые помечены метками. Чтобы получить адрес метки, Вы должны поместить вопросительный знак '?' перед именем метки.

Пример Указатели и метки

Debug "Size of data file = " + Str(?endofmydata - ?mydata)

DataSection
    mydata:
    IncludeBinary "somefile.bin"
    endofmydata:
EndDataSection