关于C#:Python ctypes和指针作为函数参数

Python ctypes and pointers as function arguments

我编写了一个C库,用于通过USB与硬件设备接口,并且已经将该库成功与其他C代码一起使用,因此我知道它可以工作。我想从Python调用此库,为此,我需要通过引用传递结构,以便库可以对其进行修改。该结构定义为:

1
2
3
4
5
6
7
8
typedef struct eventNode eventNode;
struct eventNode{
    int channels;
    int samples;
    uint16_t** event;
    eventNode* next;
    eventNode* prev;
}

并且在Python中我定义了:

1
2
3
4
5
6
7
8
class eventNode(Structure):
    pass

eventNode._fields_ = [("channels", c_int),
            ("samples", c_int),
            ("event",POINTER(POINTER(c_uint16))),
            ("prev",POINTER(eventNode)),
            ("next",POINTER(eventNode))]

我认为是正确的。本质上,我用它来实现一个链表,我的库可以通过调用签名为void getFromCAEN(int, char*, eventNode**, eventNode**)的方法来对其进行修改。最后两个参数分别是指向列表头和尾的指针。我的python代码通过调用以下命令来创建这些指针:

1
2
queueHead = POINTER(eventNode)()
queueTail = POINTER(eventNODE)()

,它最初应该为NULL(ctypes中没有),以便库知道列表为空。当我用

行调用此函数时

1
caenlib.getFromCAEN(handle,buff,byref(queueHead),byref(queueTail))

该库应形成一个链表,然后我尝试使用以下代码块对其进行索引:

1
2
3
4
5
6
7
8
9
10
cur = queueHead
while cur:
    i+=1
    print("event!",i)
    print(addressof(cur))
    print(cur.contents.channels,cur.contents.samples)
    print(cur.contents.event)
    print(cur.contents.next) #print next object in line
    cur = cur.contents.next
    print(cur)               #this prints a different address for some reason

所有这些打印语句的输出是这样的:

1
2
3
4
5
6
event! 1
140486871191712
4 150
<__main__.LP_LP_c_ushort object at 0x7fc5a60d9200>
<__main__.LP_eventNode object at 0x7fc5a60d9170>
<__main__.LP_eventNode object at 0x7fc5a60d9290>

但是问题是,即使我知道列表中应该有很多事件,它也只会迭代一次然后停止。问题似乎出在最后几行,即使cur被分配给cur.contents.next,地址也似乎在分配后从0x ... 170变为0x ... 290。在此之后尝试取消引用cur会引发NULL poitner异常。我认为我缺少有关Python中的指针和赋值的信息,但是我不确定该怎么做。

PS对cur.contents.channelscur.contents.samples的访问将完全返回我对我可以访问的一个对象的期望值,表明它至少在部分工作。


eventNode的ctypes定义中,已经翻转了nextprev。因此,当您使用next时,实际上得到的是prevNULL值。

更改中的LP_eventNode地址无关。这是每次您访问Structure字段时创建的Python对象的地址。

1
2
3
4
5
6
7
8
9
10
11
12
from ctypes import *

class eventNode(Structure):
    pass

eventNode._fields_ = [
    ("channels", c_int),
    ("samples", c_int),
    ("event", POINTER(POINTER(c_uint16))),
    ("next", POINTER(eventNode)),
    ("prev", POINTER(eventNode)),
]

_fields_被定义为tuplelist,以便保留顺序以计算每个字段的偏移量。类型中的CField描述符指定ctypes类型,大小和每个字段的偏移量。