Использование указателей в Python с использованием ctypes

Свойства Python Изучение

В этой статье мы узнаем об использовании указателей в Python с помощью модуля ctypes.

Мы увидим, как мы можем использовать указатели в языке программирования Python с помощью модуля ctypes. Здесь будут продемонстрированы некоторые основные операции, такие как сохранение адресов, указание на другую переменную с помощью указателей и т. д. Разработчики, которые использовали C или C++, могут быть знакомы с концепцией указателей и знают, что, хотя иногда ее трудно понять, она очень полезна.

Что такое указатель?

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

Кроме того, если мы хотим сохранить адрес целочисленной переменной, то наш указатель также должен иметь тип 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
print(value_1)
 
# printing the value which value_1 stores
print(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
print(float_value,double_value)
 
# printing the values stored inside float_value and double_value variable
print(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)
 
print("Contents of value_1 : " ,
      value_1)
print("Real value stored in value_1 : ",
      value_1.value)
print("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
print("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
print("The value at which ptr points at : ",
      ptr.contents.value)
 
# Printing the address of the value to 
# which the ptr variable points at
print("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
print("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
print("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
print(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
print("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
print("Contents of void pointer after \
pointing it to a variable : ",ptr2.contents)
 
# Printing the value stored in the Void Pointer
print("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
print(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
print(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
print("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
print("Boolean Value of Null Pointer",
      bool(null_ptr))

Объяснение: здесь мы проверяем, действительно ли мы смогли успешно создать нулевой указатель. Для этой цели можно использовать функцию Python bool(), логическое значение нулевого указателя всегда будет равно 0, т.е. False, что будет означать, что мы успешно создали нулевой указатель.

Вывод:

Boolean Value of Void Pointer True
Boolean Value of Null Pointer False

Оцените статью
bestprogrammer.ru
Добавить комментарий