Criando e Usando Structs C com ctypes
Nesta etapa, aprenderemos como definir e usar estruturas C em Python. Structs são essenciais ao trabalhar com APIs do sistema, pois são comumente usadas para trocar dados entre Python e bibliotecas do sistema.
Compreendendo Estruturas C em Python
Estruturas C podem ser representadas em Python usando a classe ctypes.Structure. Isso nos permite criar estruturas de dados complexas que correspondem ao layout de memória esperado pelas funções C.
Vamos criar um arquivo chamado struct_example.py para explorar como trabalhar com estruturas:
import ctypes
## Define a simple C structure
class Point(ctypes.Structure):
_fields_ = [
("x", ctypes.c_int),
("y", ctypes.c_int)
]
## Create an instance of the Point structure
p = Point(10, 20)
print(f"Point coordinates: ({p.x}, {p.y})")
## Modify structure fields
p.x = 100
p.y = 200
print(f"Updated coordinates: ({p.x}, {p.y})")
## Create a Point from a dictionary
values = {"x": 30, "y": 40}
p2 = Point(**values)
print(f"Point from dictionary: ({p2.x}, {p2.y})")
## Get the raw memory view (pointer) of the structure
p_ptr = ctypes.pointer(p)
print(f"Memory address of p: {ctypes.addressof(p)}")
print(f"Access via pointer: ({p_ptr.contents.x}, {p_ptr.contents.y})")
## Define a nested structure
class Rectangle(ctypes.Structure):
_fields_ = [
("top_left", Point),
("bottom_right", Point),
("color", ctypes.c_int)
]
## Create a rectangle with two points
rect = Rectangle(Point(0, 0), Point(100, 100), 0xFF0000)
print(f"Rectangle: Top-Left: ({rect.top_left.x}, {rect.top_left.y}), "
f"Bottom-Right: ({rect.bottom_right.x}, {rect.bottom_right.y}), "
f"Color: {hex(rect.color)}")
## Calculate the area (demonstrating structure manipulation)
width = rect.bottom_right.x - rect.top_left.x
height = rect.bottom_right.y - rect.top_left.y
print(f"Rectangle area: {width * height}")
Execute o script:
python3 struct_example.py
Você deve ver uma saída semelhante a esta:
Point coordinates: (10, 20)
Updated coordinates: (100, 200)
Point from dictionary: (30, 40)
Memory address of p: 140737345462208
Access via pointer: (100, 200)
Rectangle: Top-Left: (0, 0), Bottom-Right: (100, 100), Color: 0xff0000
Rectangle area: 10000
Criando um Utilitário de Tempo do Sistema
Agora, vamos criar uma aplicação prática que usa estruturas C para interagir com as funções de tempo do sistema. Crie um arquivo chamado time_utility.py:
import ctypes
import time
from datetime import datetime
## Define the timespec structure (used in Linux for high-resolution time)
class timespec(ctypes.Structure):
_fields_ = [
("tv_sec", ctypes.c_long),
("tv_nsec", ctypes.c_long)
]
## Define the tm structure (used for calendar time representation)
class tm(ctypes.Structure):
_fields_ = [
("tm_sec", ctypes.c_int), ## seconds (0 - 60)
("tm_min", ctypes.c_int), ## minutes (0 - 59)
("tm_hour", ctypes.c_int), ## hours (0 - 23)
("tm_mday", ctypes.c_int), ## day of month (1 - 31)
("tm_mon", ctypes.c_int), ## month of year (0 - 11)
("tm_year", ctypes.c_int), ## year - 1900
("tm_wday", ctypes.c_int), ## day of week (0 - 6, Sunday = 0)
("tm_yday", ctypes.c_int), ## day of year (0 - 365)
("tm_isdst", ctypes.c_int) ## is daylight saving time in effect
]
## Load the C library
libc = ctypes.CDLL("libc.so.6")
def get_system_time():
"""Get the current system time using C functions"""
## Get current time as seconds since epoch
time_t_ptr = ctypes.pointer(ctypes.c_long())
libc.time(time_t_ptr) ## time() function gets current time
## Convert to printable time string
time_val = time_t_ptr.contents.value
time_str = ctypes.string_at(libc.ctime(time_t_ptr))
return {
"timestamp": time_val,
"formatted_time": time_str.decode().strip()
}
def get_high_resolution_time():
"""Get high resolution time using clock_gettime"""
ts = timespec()
## CLOCK_REALTIME is usually 0
CLOCK_REALTIME = 0
## Call clock_gettime to fill the timespec structure
if libc.clock_gettime(CLOCK_REALTIME, ctypes.byref(ts)) != 0:
raise OSError("Failed to get time")
return {
"seconds": ts.tv_sec,
"nanoseconds": ts.tv_nsec,
"precise_time": ts.tv_sec + (ts.tv_nsec / 1_000_000_000)
}
def time_breakdown():
"""Break down the current time into its components using localtime"""
## Get current time
time_t_ptr = ctypes.pointer(ctypes.c_long())
libc.time(time_t_ptr)
## Get local time
tm_ptr = libc.localtime(time_t_ptr)
tm_struct = ctypes.cast(tm_ptr, ctypes.POINTER(tm)).contents
## Return time components
return {
"year": 1900 + tm_struct.tm_year,
"month": 1 + tm_struct.tm_mon, ## tm_mon is 0-11, we adjust to 1-12
"day": tm_struct.tm_mday,
"hour": tm_struct.tm_hour,
"minute": tm_struct.tm_min,
"second": tm_struct.tm_sec,
"weekday": tm_struct.tm_wday, ## 0 is Sunday
"yearday": tm_struct.tm_yday + 1 ## tm_yday is 0-365, we adjust to 1-366
}
## Main program
print("Time Utility using C Structures")
print("-" * 40)
## Get and display system time
sys_time = get_system_time()
print(f"System time: {sys_time['formatted_time']}")
print(f"Timestamp (seconds since epoch): {sys_time['timestamp']}")
## Get and display high-resolution time
hi_res = get_high_resolution_time()
print(f"\nHigh resolution time:")
print(f"Seconds: {hi_res['seconds']}")
print(f"Nanoseconds: {hi_res['nanoseconds']}")
print(f"Precise time: {hi_res['precise_time']}")
## Get and display time breakdown
components = time_breakdown()
print(f"\nTime breakdown:")
print(f"Date: {components['year']}-{components['month']:02d}-{components['day']:02d}")
print(f"Time: {components['hour']:02d}:{components['minute']:02d}:{components['second']:02d}")
print(f"Day of week: {components['weekday']} (0=Sunday)")
print(f"Day of year: {components['yearday']}")
## Compare with Python's datetime
now = datetime.now()
print(f"\nPython datetime: {now}")
print(f"Python timestamp: {time.time()}")
Execute o utilitário de tempo:
python3 time_utility.py
Você deve ver uma saída semelhante a esta:
Time Utility using C Structures
----------------------------------------
System time: Wed Jun 14 15:22:36 2023
Timestamp (seconds since epoch): 1686756156
High resolution time:
Seconds: 1686756156
Nanoseconds: 923456789
Precise time: 1686756156.923457
Time breakdown:
Date: 2023-06-14
Time: 15:22:36
Day of week: 3 (0=Sunday)
Day of year: 165
Python datetime: 2023-06-14 15:22:36.923499
Python timestamp: 1686756156.9234989
Compreendendo o Gerenciamento de Memória com ctypes
Ao trabalhar com ctypes e estruturas C, é importante entender o gerenciamento de memória. Vamos criar mais um exemplo, memory_management.py, para demonstrar isso:
import ctypes
import gc ## Garbage collector module
## Define a simple structure
class MyStruct(ctypes.Structure):
_fields_ = [
("id", ctypes.c_int),
("value", ctypes.c_double),
("name", ctypes.c_char * 32) ## Fixed-size character array
]
def demonstrate_memory_management():
print("Memory Management with ctypes")
print("-" * 40)
## Create a structure instance
my_data = MyStruct(
id=1,
value=3.14159,
name=b"Example"
)
print(f"Structure size: {ctypes.sizeof(my_data)} bytes")
print(f"Memory address: {hex(ctypes.addressof(my_data))}")
## Create a pointer to the structure
data_ptr = ctypes.pointer(my_data)
print(f"Pointer value: {hex(ctypes.cast(data_ptr, ctypes.c_void_p).value)}")
## Access through pointer
print(f"Access via pointer: id={data_ptr.contents.id}, value={data_ptr.contents.value}")
## Allocate memory for a new structure
new_struct_ptr = ctypes.POINTER(MyStruct)()
new_struct_ptr = ctypes.cast(
ctypes.create_string_buffer(ctypes.sizeof(MyStruct)),
ctypes.POINTER(MyStruct)
)
## Initialize the allocated memory
new_struct = new_struct_ptr.contents
new_struct.id = 2
new_struct.value = 2.71828
new_struct.name = b"Allocated"
print(f"\nAllocated structure memory address: {hex(ctypes.addressof(new_struct))}")
print(f"Allocated structure content: id={new_struct.id}, value={new_struct.value}, name={new_struct.name.decode()}")
## Create an array of structures
StructArray = MyStruct * 3
struct_array = StructArray(
MyStruct(10, 1.1, b"First"),
MyStruct(20, 2.2, b"Second"),
MyStruct(30, 3.3, b"Third")
)
print("\nArray of structures:")
for i, item in enumerate(struct_array):
print(f" [{i}] id={item.id}, value={item.value}, name={item.name.decode()}")
## Force garbage collection
print("\nForcing garbage collection...")
gc.collect()
## Memory is automatically managed by Python
## Run the demonstration
demonstrate_memory_management()
Execute o script de gerenciamento de memória:
python3 memory_management.py
Você deve ver uma saída semelhante a esta:
Memory Management with ctypes
----------------------------------------
Structure size: 48 bytes
Memory address: 0x7f3c2e32b040
Pointer value: 0x7f3c2e32b040
Access via pointer: id=1, value=3.14159
Allocated structure memory address: 0x7f3c2e32b0a0
Allocated structure content: id=2, value=2.71828, name=Allocated
Array of structures:
[0] id=10, value=1.1, name=First
[1] id=20, value=2.2, name=Second
[2] id=30, value=3.3, name=Third
Forcing garbage collection...
Nestes exemplos, aprendemos como:
- Definir e usar estruturas C em Python com
ctypes
- Criar ponteiros para estruturas e acessar seus conteúdos
- Criar estruturas aninhadas e matrizes de estruturas
- Construir aplicações práticas usando funções de tempo do sistema
- Gerenciar memória ao trabalhar com
ctypes
Essas habilidades são essenciais ao trabalhar com funções da API do Windows, que frequentemente exigem estruturas de dados complexas para passar informações de um lado para o outro.