Сообщить об ошибке.

Указатели Pointer модуля ctypes в Python

Приватный абстрактный базовый класс указателей.

ctypes._Pointer:

Конкретные типы указателей создаются путем вызова функции ctypes.POINTER() с типом, на который они будут указывать. Это делается автоматически с помощью функции ctypes.pointer().

Если указатель указывает на массив, то его элементы могут быть прочитаны и записаны с использованием стандартного доступа по индексу и срезу.

Объекты-указатели не имеют размера, поэтому функция len() вызовет исключение TypeError.

Отрицательные индексы будут считываться из памяти перед указателем (как в C), а индексы вне диапазона, сломаются при нарушении прав доступа.

Атрибуты класса ctypes._Pointer

Pointer._type_:

Атрибут Pointer._type_ возвращает тип, на который указывает указатель.

Pointer.contents:

Атрибут Pointer.contents возвращает объект, на который указывает указатель.

Присвоение этому атрибуту - изменяет указатель на вновь назначенный объект.


Примеры использования указателей модуля ctypes.

Экземпляры указателей создаются путем вызова функции ctypes.pointer() для типа модуля ctypes:

>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)

Экземпляры указателей имеют атрибут Pointer.contents, который возвращает объект, на который указывает указатель. Смотрим на что указывает созданный указатель pi в коде выше:

>>> pi.contents
# c_long(42)

Обратите внимание, что модуль ctypes не имеет OOR (возврат исходного объекта), он создает новый эквивалентный объект каждый раз, когда извлекается атрибут:

>>> pi.contents is i
# False
>>> pi.contents is pi.contents
# False

Назначение другого экземпляра ctypes.c_int атрибуту указателя .contents приведет к тому, что он будет указывать на то место памяти, где он хранится:

>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
# c_long(99)

Экземпляры указателя также индексируются целыми числами:

>>> pi[0]
# 99

Присвоение целочисленному индексу - изменяет указанное значение:

>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)

Также можно использовать индексы, отличные от 0, но при этом необходимо понимать что вы делаете. Как и в C: можно получить доступ к произвольным ячейкам памяти или изменить их. Обычно эту функциональность используют только в том случае, если получают указатель от C-функции, который фактически указывает на массив, а не на отдельный элемент.

За кулисами функция ctypes.pointer() делает больше, чем просто создает экземпляры указателя, она должна сначала создать типы указателей. Это делается с помощью функции ctypes.POINTER(), которая принимает любой тип ctypes и возвращает новый тип:

>>> PI = POINTER(c_int)
>>> PI
# <class 'ctypes.LP_c_long'>
>>> PI(42)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: expected c_long instead of int
>>> PI(c_int(42))
# <ctypes.LP_c_long object at 0x...>

Вызов типа указателя без аргумента создает нулевой указатель. Нулевые указатели имеют ложное логическое значение:

>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
# False

Модуль ctypes проверяет NULL при разыменовании указателей, но разыменование недопустимых указателей, отличных от NULL, приведет к сбою Python:

>>> null_ptr[0]
# Traceback (most recent call last):
    ....
# ValueError: NULL pointer access

>>> null_ptr[0] = 1234
# Traceback (most recent call last):
    ....
# ValueError: NULL pointer access

Передача указателей или передача параметров по ссылке.

Иногда функция api языка C ожидает указатель на тип данных в качестве параметра, для записи в соответствующее место или, если данные слишком велики, для передачи их по значению. Это также известно как передача параметров по ссылке.

Модуль ctypes экспортирует функцию ctypes.byref(), которая используется для передачи параметров по ссылке. Тот же эффект может быть достигнут с помощью функции ctypes.pointer(), хотя ctypes.pointer() выполняет гораздо больше работы, т.к. создает реальный объект-указатель, поэтому функция ctypes.byref() работает быстрее, если конечно не нужен сам объект-указатель:

>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
# 0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
...             byref(i), byref(f), s)
# 3
>>> print(i.value, f.value, repr(s.value))
# 1 3.1400001049 b'Hello'