В этой статье мы узнаем об использовании указателей в Python с помощью модуля ctypes.
Мы увидим, как мы можем использовать указатели в языке программирования Python с помощью модуля ctypes. Здесь будут продемонстрированы некоторые основные операции, такие как сохранение адресов, указание на другую переменную с помощью указателей и т. д. Разработчики, которые использовали C или C++, могут быть знакомы с концепцией указателей и знают, что, хотя иногда ее трудно понять, она очень полезна.
- Что такое указатель?
- Создание переменной для хранения значения с помощью модуля ctypes
- Указание на другую переменную
- Изменение значения переменной, на которую указывает указатель, с помощью Pointer
- Пустые указатели
- Пустые указатели с использованием ctypes
- Заставить указатель void указывать на адрес
- Нулевой указатель
- Нулевой указатель с использованием модуля ctypes:
- Как проверить, является ли это нулевым указателем
Что такое указатель?
Указатель — это особый тип переменной, в которой хранится адрес памяти другой переменной. Они не могут хранить обычные значения, такие как целые числа, числа с плавающей запятой или строки, они могут хранить только адреса, и мы можем использовать этот адрес для печати значения, которое было сохранено в этом адресе. Указатели не хранят никакого значения, они хранят только адрес.
Кроме того, если мы хотим сохранить адрес целочисленной переменной, то наш указатель также должен иметь тип integer, для float указатель должен быть типа float. Мы не можем сохранить адрес строки в указатель на целое число.
В таких языках, как C/C++, широко используются указатели, чтобы получить значение, на которое указывает указатель, мы разыменовываем эту переменную указателя, т. е. при ее печати мы помещаем определенный спецификатор формата, а затем используем звездочку (*) с этой переменной, чтобы получить значение. значение, на которое он указывал. Чтобы получить адрес, мы просто печатаем это, используя беззнаковый спецификатор (%u), так как адреса должны быть беззнаковыми (положительные значения).
Создание переменной для хранения значения с помощью модуля ctypes
В этом мы собираемся создать указатель value_1 с помощью модуля ctypes, а затем вывести его тип и значение.
Python3
import
ctypes as ct
# value_1 variable stores an integer
# of type Ctypes (not a regular integer)
value_1
=
ct.c_int(
10
)
# printing the type of value_1
(value_1)
# printing the value which value_1 stores
(value_1.value)
Вывод: в приведенном выше коде, когда мы печатаем «value_1», он печатает c_long (10) вместо c_int (10), это так, потому что размер c_long () и c_int () одинаковый, поэтому всякий раз, когда мы пытаемся напечатать c_int () печатает c_long(). После этого мы печатаем его значение.
c_long(10) 10
Использование типа float и double:
Python3
# Importing ctypes module
import
ctypes as ct
# creating a ctypes float variable
float_value
=
ct.c_float(
15.25
)
# creating a ctypes double variable
double_value
=
ct.c_double(
85.69845
)
# printing what float_value and double_value variable holds
(float_value,double_value)
# printing the values stored inside float_value and double_value variable
(float_value.value,double_value.value)
Вывод:
c_float(15.25) c_double(85.69845) 15.25 85.69845
Указание на другую переменную
Теперь пришло время сделать то, для чего используются указатели, т.е. указать на адрес/ячейку памяти. Кроме того, мы проверим, что переменная указателя ptr, которая ссылается на указатель value_1, печатает тот же адрес или нет.
Python3
import
ctypes as ct
# storing a ctypes long value
value_1
=
ct.c_long(
10
)
# using pointer() method we are pointing to the
# value_1 variable and storing it in ptr
ptr
=
ct.pointer(value_1)
(
"Contents of value_1 : "
,
value_1)
(
"Real value stored in value_1 : "
,
value_1.value)
(
"Address of value_1 : "
,
id
(value_1.value))
# If we want to print the contents of a pointer
# type variable then need to use .contents
# otherwise only writing the variable is enough like above
(
"Contents of ptr variable : "
,
ptr.contents)
# To print the value stored in the address
# pointed by that pointer variable
# we need to use .value after .contents
(
"The value at which ptr points at : "
,
ptr.contents.value)
# Printing the address of the value to
# which the ptr variable points at
("Address of that value which
is
\
pointed
and
stored
in
ptr : ",
id
(ptr.contents.value))
Объяснение: после импорта и сохранения значения в переменной value_1 мы используем переменную с именем ptr (напоминает указатель) и используем метод указателя модуля ctypes, чтобы указать на это значение_1. Теперь, чтобы напечатать то, что содержит переменная ptr, мы должны использовать другой метод с именем contents, который будет печатать содержимое переменной, на которую указывает ptr (аналогично использованию & в C/C++), а не значение ptr. Чтобы напечатать значение ptr, мы должны использовать другой метод под названием values после содержимого, это напечатает значение, на которое указывает переменная ptr (аналогично использованию * в C/C++).
Вывод:
Contents of value_1 : c_long(10) Real value stored in value_1 : 10 Address of value_1 : 139741265425760 Contents of ptr variable : c_long(10) The value at which ptr points at : 10 Address of that value which is pointed and stored in ptr : 139741265425760
В выводе мы видим, что идентификатор последнего оператора печати и третьего оператора печати совпадают, что означает, что ptr указывает на одно и то же место в памяти.
Изменение значения переменной, на которую указывает указатель, с помощью Pointer
Здесь мы сначала создаем переменную value_1, в которой хранится значение 10, имеющее ctype long, а затем, используя переменную ptr, мы указываем на эту переменную. Теперь мы печатаем значение, хранящееся в переменной value_1, затем мы меняем значение переменной value_1, используя переменную ptr. Затем, если мы снова напечатаем значение переменной value_1, мы увидим, что значение, хранящееся внутри нее, изменилось.
Python3
import
ctypes as ct
value_1
=
ct.c_long(
10
)
# Creating a pointer which points at a
# variable which stores c_long type value
ptr
=
ct.pointer(value_1)
# Printing to see what the void pointer currently holds
(
"Value before Changing : "
,
value_1.value)
# storing a value in ptr variable
ptr.contents.value
=
25
# If we print the value_1 variable it will
# now store the updated value
(
"Value after Changing : "
,
value_1.value)
Вывод:
Value before Changing : 10 Value after Changing : 25
Пустые указатели
Как следует из названия, указатель void — это тип указателя, с которым не связан какой-либо конкретный тип данных. Он может быть приведен к любому типу и может содержать данные любого типа. Поскольку в Python концепция указателей недоступна или не используется должным образом, мы не можем правильно создать Void Pointer, поскольку функция POINTER требует одного аргумента, т. е. типа данных. Но мы можем создать указатель Void для определенного типа данных, а затем не указывать его ни на какой адрес памяти. Мы просто приводим его к типу, как C/C++ на раннем этапе.
Пустые указатели с использованием ctypes
Мы также можем создать несколько пустых указателей определенного типа, которые не содержат никакого адреса при создании.
Python3
import
ctypes as ct
# Creating a variable ptr2 which will act as a void pointer
# We will not point it to any other memory location
ptr2
=
ct.POINTER(ct.c_int)
# Trying to print a void pointer
(ptr2)
Вывод:
<class '__main__.LP_c_int'>
Как мы видим, если мы попытаемся напечатать этот указатель void, поскольку он не указывает ни на какое значение, он просто указывает на тип, который он может хранить, т.е. long (ctypes по умолчанию преобразует int в long). Обратите внимание, здесь мы использовали POINTER() вместо указателя, потому что функция pointer() создает новый экземпляр указателя, указывающий на объект. Напротив, POINTER() — это фабричная функция, которая создает и возвращает новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри, параметр, который мы будем передавать в POINTER(), должен иметь ctypes.
Заставить указатель void указывать на адрес
Теперь мы заставим указатель void указывать на переменную того же типа, которая была передана в качестве аргумента.
Python3
import
ctypes as ct
value_1
=
ct.c_int(
10
)
# Creating a void Pointer ptr2
ptr2
=
ct.POINTER(ct.c_int)
# Trying to print the contents of the void
# pointer before pointing it to any variable
(
"Contents of Void Pointer : "
,
ptr2.contents)
# Now pointing the ptr2 variable to the value_1
# variable which is a ctype c_int
ptr2.contents
=
value_1
# Now again printing the contents of the ptr2 variable
("Contents of void pointer after \
pointing it to a variable : ",ptr2.contents)
# Printing the value stored in the Void Pointer
(
"Value stored by Void Pointer : "
,
ptr2.contents.value)
Объяснение: после создания Void Pointer мы указываем этой переменной ptr2 переменную ctype c_int value_1. Теперь, если мы напечатаем содержимое переменной ptr2, мы увидим, что она содержит c_long(10). Теперь, если мы попытаемся напечатать значение, хранящееся в адресе, на который указывает указатель ex-void, мы получим 10 в качестве вывода.
Вывод:
Contents of Void Pointer : <attribute 'contents' of '_ctypes._Pointer' objects> Contents of void pointer after pointing it to a variable : c_int(10) Value stored by Void Pointer : 10
Если мы не укажем на место в памяти, а попытаемся напечатать значение указателя void, мы получим ошибку, как показано ниже.
Python3
import
ctypes as ct
value_1
=
ct.c_int(
10
)
ptr
=
ct.pointer(value_1)
# Creating a void pointer
ptr2
=
ct.POINTER(ct.c_int)
# Trying to print the value of a void pointer
# This returns an error
(ptr2.contents.value)
Вывод:
Traceback (most recent call last): File "5e9a4694-422e-49db-afcd-212fcd2bfa3e.py", line 12, in <module> print(ptr2.contents.value) AttributeError: 'getset_descriptor' object has no attribute 'value'
Нулевой указатель
Нулевой указатель также является типом указателя, который не указывает ни на какое место в памяти. Разница между нулевым указателем и пустым указателем заключается в том, что мы не можем привести нулевой указатель к любому другому типу данных, например к пустому указателю.
Нулевой указатель с использованием модуля ctypes:
Мы также можем создать нулевой указатель с помощью модуля ctypes, если мы создадим переменную типа POINTER без каких-либо аргументов, она создаст нулевой указатель.
Python3
import
ctypes as ct
# Creating a Null Pointer of type c_long
null_ptr
=
ct.POINTER(ct.c_long)()
# Printing the null pointer
(null_ptr)
Здесь мы создаем нулевой указатель типа c_long, предоставляя пробел () после метода POINTER(), который указывает, что это нулевой указатель. Теперь, если мы попытаемся распечатать его, мы получим шестнадцатеричный адрес памяти, как показано ниже.
Вывод:
<__main__.LP_c_long object at 0x7f5df5740200>
Как проверить, является ли это нулевым указателем
Мы можем проверить нулевой указатель с помощью функции bool(), логическое значение нулевого указателя всегда равно False,
Python3
# Importing ctypes module
import
ctypes as ct
# Declare a void pointer
void_ptr
=
ct.POINTER(ct.c_int)
# Printing the boolean value of void pointer
(
"Boolean Value of Void Pointer"
,
bool
(void_ptr))
# Declare a null pointer
null_ptr
=
ct.POINTER(ct.c_int)()
# Printing the boolean value of null pointer
(
"Boolean Value of Null Pointer"
,
bool
(null_ptr))
Объяснение: здесь мы проверяем, действительно ли мы смогли успешно создать нулевой указатель. Для этой цели можно использовать функцию Python bool(), логическое значение нулевого указателя всегда будет равно 0, т.е. False, что будет означать, что мы успешно создали нулевой указатель.
Вывод:
Boolean Value of Void Pointer True Boolean Value of Null Pointer False