ctypes objects own only the memory that they allocate. Inspect the _b_needsfree_ attribute to determine whether a ctypes object owns the referenced memory.
For example:
This array object owns a 12-byte buffer for the array elements:
>>> arr = (ctypes.c_uint * 3)(0, 1, 2)
>>> arr._b_needsfree_
1
This pointer object owns an 8-byte buffer for the 64-bit target address:
>>> p = ctypes.POINTER(ctypes.c_uint * 3)(arr)
>>> p._b_needsfree_
1
The following new array object created by dereferencing the pointer does not own the 12-byte buffer:
>>> ref = p[0]
>>> ref[:]
[0, 1, 2]
>>> ref._b_needsfree_
0
However, it does have a reference chain back to the original array:
>>> ref._b_base_ is p
True
>>> p._objects['1'] is arr
True
On the other hand, if you use the from_address() class method, the resulting object is a dangling reference to memory that it doesn't own and for which there's no supporting reference chain to the owning object.
For example:
>>> arr = (ctypes.c_uint * 2**24)()
>>> arr[-1] = 42
>>> ref = type(arr).from_address(ctypes.addressof(arr))
>>> ref[-1]
42
>>> ref._b_base_ is None
True
>>> ref._objects is None
True
2**24 bytes is a big allocation that uses mmap (Unix) instead of the heap. Thus accessing ref[-1] causes a segfault after arr is
deallocated:
>>> del arr
>>> ref[-1]
Segmentation fault (core dumped)