访问递归Python ctypes结构的分段错误

时间:2015-05-27 15:23:32

标签: python ctypes

我无法使用Python ctypes访问嵌入在其他结构中的结构中的元素。

这是C:

struct GSList
{
    void* data;
    GSList* next;
};

struct att_range
{
    uint16_t start;
    uint16_t end;
};

struct gatt_primary
{
    char uuid[38];
    int changed;
    struct att_range* range;
};

typedef void( *python_callback_t )( GSList* services );

static python_callback_t pycb = NULL;

// pycb is set to a valid function pointer before the callback below is called.

static void primary_all_cb( GSList *services, uint8_t status )
{
    if( status == 0 ) {
        pycb( services );
    }
}

这是Python:

import ctypes

# CDLL is used to access a shared library that contains the C code above

class range(ctypes.Structure):
    _fields_ = [
        ('starthnd', ctypes.c_uint16),
        ('endhnd', ctypes.c_uint16)
    ]

class service(ctypes.Structure):
    _fields_ = [
        ('uuid', ctypes.c_char_p),
        ('changed', ctypes.c_int),
        ('range', ctypes.POINTER(range))
    ]

class servicelist(ctypes.Structure):
    pass
servicelist._fields_ = [
    ('data', ctypes.POINTER(service)),
    ('next', ctypes.POINTER(servicelist))
]

CB_FUNC_TYPE = ctypes.CFUNCTYPE(None, ctypes.POINTER(servicelist))
def cb(csvclist):
    svclist = ctypes.cast(csvclist, ctypes.POINTER(servicelist))
    data = ctypes.cast(svclist.contents.data, ctypes.POINTER(service))
    next = ctypes.cast(svclist.contents.next, ctypes.POINTER(servicelist))
    hndrange = ctypes.cast(data.contents.range, ctypes.POINTER(range))
    starthnd = ctypes.cast(hndrange.contents.starthnd, ctypes.c_uint16)
    endhnd = ctypes.cast(hndrange.contents.endhnd.contents,  ctypes.c_uint16)
    uuid = ctypes.cast(data.contents.uuid, ctypes.c_char_p)
    print('start: 0x%X, end: 0x%X, uuid: %s' % (starthnd, endhnd, uuid))
cb_func = CB_FUNC_TYPE(cb)

// gatt is the shared library. ble_start source is not shown here, but it sets the callback function pointer.
gatt.ble_start(cb_func)

我正在使用Python 3.4和gcc 4.7。这不包括最初调用以触发回调函数的函数或用于访问共享库的Python代码。我已经验证了所有信息都填充了C中的结构,并且能够在某一点上用Python打印uuid的内容。当我尝试访问hndrange时出现分段错误。我可以在Python中打印出hndrange的对象引用,但是如果我尝试访问这些元素,我会遇到分段错误。

我做错了什么?

感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

您的service课程与gatt_primary结构不匹配。 uuid应该是char的数组,而不是指针:

class service(ctypes.Structure):
    _fields_ = [
        ('uuid', ctypes.c_char*38),
        ('changed', ctypes.c_int),
        ('range', ctypes.POINTER(range))
    ]

除此之外,对结构的返回字段使用强制转换也不是一个好主意。查看Structures and unions文档,您会发现,fundamental data types返回了相关的python类型。所有其他派生类型都按原样返回。

因此,例如ctypes.cast(hndrange.contents.starthnd, ctypes.c_uint16)会尝试将python int类型转换为ctypes.c_uint16