Ctypes: Get a pointer to a struct field

2019-08-15 03:06发布

问题:

I need to get a pointer with the address of a struct field. Important: I'm builduing a serializer for a set of c structs so i want to iterate over the fields of a struct and get the address of each of them as a pointer. I know there is a way using fields object and offset property of them which is giving you the offset from the address of the structure itself but it is a generic pointer. Could you show me a way on how to iterate over struct fields and for each of them get a ctypes pointer with a correct inner type?

回答1:

Here's a way...

The class variable Test._fields_ defines the attributes and their C types. The class attributes generated by assigning the fields contain information about the offset for that attribute, for example:

>>> Test.a
<Field type=c_long, ofs=0, size=4>
>>> Test.b
<Field type=c_double, ofs=8, size=8>
>>> Test.c
<Field type=c_char_Array_10, ofs=16, size=10>

For each type, the from_buffer() method builds a C type using the same data buffer as an existing type. An instance of the structure implements the buffer API required to access its data, so if you know the offset of a structure element's data, you can generate a ctypes type that references the same data, and create a pointer to it.

from ctypes import *

class Test(Structure):
    _fields_ = [('a',c_int),
                ('b',c_double),
                ('c',c_char * 10)]

x = Test(1,2.5,b'abc')

for field,ftype in Test._fields_:

    # Look up each attribute in the class, and get its offset.
    ofs = getattr(Test,field).offset

    # Create a pointer to the same field type using the same data.
    p = pointer(ftype.from_buffer(x,ofs))
    print(p,p.contents)

Output:

<__main__.LP_c_long object at 0x000001932DE0EEC8> c_long(1)
<__main__.LP_c_double object at 0x000001932DE0ECC8> c_double(2.5)
<__main__.LP_c_char_Array_10 object at 0x000001932DE0EDC8> <__main__.c_char_Array_10 object at 0x000001932DE0ECC8>