News from this site

 Rental advertising space, please contact the webmaster if you need cooperation


+focus
focused

classification  

no classification

tag  

no tag

date  

no datas

When does ctypes free memory?

posted on 2024-12-02 22:11     read(92)     comment(0)     like(0)     collect(0)


In Python I'm using ctypes to exchange data with a C library, and the call interface involves nested pointers-to-structs.

If the memory was allocated from in C, then python should (deeply) extract a copy of any needed values and then explicitly ask that C library to deallocate the memory.

If the memory was allocated from in Python, presumably the memory will be deallocated soon after the corresponding ctypes object passes out of scope. How does this work for pointers? If I create a pointer object from a string buffer, then do I need to keep a variable referencing that original buffer object in scope, to prevent this pointer from dangling? Or does the pointer object itself automatically do this for me (even though it won't return the original object)? Does it make any difference whether I'm using pointer, POINTER, cast, c_void_p, or from_address(addressof)?


solution


Nested pointers to simple objects seem fine. The documentation is explicit that ctypes doesn't support "original object return", but implies that the pointer does store a python-reference in order to keep-alive its target object (the precise mechanics might be implementation-specific).

>>> from ctypes import *
>>> x = c_int(7)
>>> triple_ptr = pointer(pointer(pointer(x)))
>>> triple_ptr.contents.contents.contents.value == x.value
True
>>> triple_ptr.contents.contents.contents is x
False
>>> triple_ptr._objects['1']._objects['1']._objects['1'] is x # CPython 3.5
True 

Looks like the pointer function is no different to the POINTER template constructor (like how create_string_buffer relates to c_char * size).

>>> type(pointer(x)) is type(POINTER(c_int)(x))
True

Casting to void also seems to keep the reference (but I'm not sure why it modifies the original pointer?).

>>> ptr = pointer(x)
>>> ptr._objects
{'1': c_int(7)}
>>> pvoid = cast(p, c_void_p)
>>> pvoid._objects is ptr._objects
True
>>> pvoid._objects
{139665053613048: <__main__.LP_c_int object at 0x7f064de87bf8>, '1': c_int(7)}
>>> pvoid._objects['1'] is x
True

Creating an object directly from a memory buffer (or address thereof) looks more fraught.

>>> v = c_void_p.from_buffer(triple_ptr)
>>> v2 = c_void_p.from_buffer_copy(triple_ptr)
>>> type(v._objects)
<class 'memoryview'>
>>> POINTER(POINTER(POINTER(c_int))).from_buffer(v)[0][0][0] == x.value
True
>>> p3 = POINTER(POINTER(POINTER(C_int))).from_address(addressof(triple_ptr))
>>> v2._objects is None is p3._objects is p3._b_base_
True

Incidentally, byref probably keeps-alive the memory it references.

>>> byref(x)._obj is x
True


Category of website: technical article > Q&A

Author:qs

link:http://www.pythonblackhole.com/blog/article/247243/4f517cc6e8cd7930921c/

source:python black hole net

Please indicate the source for any form of reprinting. If any infringement is discovered, it will be held legally responsible.

0 0
collect article
collected

Comment content: (supports up to 255 characters)