c和python之间的数据损坏

2024-05-23 14:23:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用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)

输出:

Stream is d3 ff f7 7f 0 0 5f # expected 0xD3,0xFF,0xF7,0x7F,0x00,0x00,0x41

解决方案:

1。 我不得不将cython更新为0.29,以修复不允许使用类型化内存的错误(只读问题)。你知道吗

2。 它通过了x_生的. 但是当x_ca.价值已传递,但抛出错误“越界访问”

根据@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])

''测试.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)

输出:

Stream is 47 d3 ff f7 7f 0 0 41


Tags: streamreturnisfunctionctypescadummypyx
1条回答
网友
1楼 · 发布于 2024-05-23 14:23:15

正如@DavidW指出的,问题在于x_ca.value的用法:调用x_ca.value时,每次创建新的bytes对象(请参见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的bytes对象,它不与x_ca共享内存—当您访问stream[6]时,它会导致未定义的行为—任何事情都可能发生(也可能发生崩溃)。你知道吗


那该怎么办呢?你知道吗

通常情况下,在def函数中不能有指针参数,但char *是一个例外bytes对象可以自动转换为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传递给期望const unsigned char *作为参数的函数,正如@DavidW在注释中指出的那样,它与x_ca共享内存。不过,我更喜欢类型化内存视图—它们比原始指针更安全,而且不会出现意外的未定义行为。你知道吗

相关问题 更多 >