Указатели и доступ к памяти
Указатели '*'
Указатели - это переменные специального (указательного) типа, которые хранят адрес объекта в памяти. По этому адресу может находиться Переменная, Массив, Связный список, Хеш-карта, Структура, Процедура или Метка. В начале имени такой переменной находится символ '*' (звёздочка). То есть, *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)
Указатели позволяют производить перемещение, чтение и запись в памяти. Кроме того они позволяют программисту обратиться к большому объёму данных, без дополнительных затрат на копирование данных. Копирование указателя намного быстрее, так как он занимает всего 4 или 8 байт.
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
Указатели также доступы в структурах, для получения дополнительной информации смотрите главу структур.
У всех переменных есть постоянный размер в памяти (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