我正在尝试使用Cython和ctypes
来使用Python调用c库函数。
但是数据字节以某种方式损坏。有人可以帮忙找到问题吗?
testCRC.c
:
#include <stdio.h>
unsigned char GetCalculatedCrc(const unsigned char* stream){
printf("Stream is %x %x %x %x %x %x %x\n",stream[0],stream[1],stream[2],stream[3],stream[4],stream[5],stream[6]);
unsigned char dummy=0;
return dummy;
}
wrapped.pyx
:
# Exposes a c function to python
def c_GetCalculatedCrc(const unsigned char* stream):
return GetCalculatedCrc(stream)
test.py
:
x_ba=(ctypes.c_ubyte *7)(*[0xD3,0xFF,0xF7,0x7F,0x00,0x00,0x41])
x_ca=(ctypes.c_char * len(x_ba)).from_buffer(x_ba)
y=c_GetCalculatedCrc(x_ca.value)
输出:
流是d3 ff f7 7f 0 0 5f#预期 0xD3,0xFF,0xF7,0x7F,0x00,0x00,0x41
解决方案:
1。 我不得不将cython更新为0.29,以修复不允许使用键入的内存的错误。(只读问题)。
2。 它通过x_ca.raw起作用。但是,当传递x_ca.value时,它会引发错误“访问权限超出限制”。
根据@ead和@DavidW的建议:
´.pyx´:
def c_GetCalculatedCrc(const unsigned char[:] stream):
# Exposes a c function to python
print "received %s\n" %stream[6]
return GetCalculatedCrc(&stream[0])
“ test.py”:
x_ba=(ctypes.c_ubyte *8)(*[0x47,0xD3,0xFF,0xF7,0x7F,0x00,0x00,0x41])
x_ca=(ctypes.c_char * len(x_ba)).from_buffer(x_ba)
y=c_GetCalculatedCrc(x_ca.raw)
输出:
流是47 d3 ff f7 7f 0 0 41
答案 0 :(得分:3)
@DavidW指出,问题是您使用x_ca.value
:调用x_ca.value
时,每次创建新的字节对象(请参见documentation)并且内存为复制:
x_ca.value is x_ca.value
#False -> every time a new object is created
但是,在复制内存时,它将\0
字符作为字符串的结尾(对于C字符串来说是典型的),如source code所示:
static PyObject *
CharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored))
{
Py_ssize_t i;
char *ptr = self->b_ptr;
for (i = 0; i < self->b_size; ++i)
if (*ptr++ == '\0')
break;
return PyBytes_FromStringAndSize(self->b_ptr, i);
}
因此,x_ca.value
的结果是一个字节4
的字节对象,当您访问{{1时,它不不与x_ca
共享内存}}会导致未定义的行为-可能发生任何事情(也是崩溃)。
那该怎么办?
通常,stream[6]
函数中不能包含指针参数,但是def
是一个例外-char *
对象可以自动转换为bytes
,但这不是通过缓冲区协议而是通过PyBytes_AsStringAndSize
来实现的。
这就是为什么您不能按原样将char *
传递给x_ca
的原因:c_GetCalculatedCrc
实现了缓冲区协议,但不是x_ca
对象,因此没有bytes
。
另一种方法是使用类型化的内存视图,该视图利用了缓冲协议,即
PyBytes_AsStringAndSize
,现在以原始长度/内容直接传递%%cython
def c_GetCalculatedCrc(const unsigned char[:] stream):
print(stream[6]);
:
x_ca
另一种选择是将c_GetCalculatedCrc(x_ca)
# 65 as expected
传递给以x_ca.raw
为参数的函数,正如@DavidW在评论中指出的那样,该函数与const unsigned char *
共享内存。但是,我更喜欢类型化的内存视图-它们比原始指针更安全,并且不会遇到意外的未定义行为。