【发布时间】:2020-12-17 12:30:30
【问题描述】:
我在以下测试程序中观察到与 ctypes 相关的奇怪行为:
import ctypes as ct
def _pyfunc(a_c_string):
print(type(a_c_string))
a_c_string.value = b"87654321"
return -123
my_str_buf = ct.create_string_buffer(b"test1234")
print(type(my_str_buf))
my_str_buf[3] = b'*'
print(my_str_buf.value)
my_str_buf.value = b"4321test"
print(my_str_buf.value)
signature = ct.CFUNCTYPE(ct.c_int, ct.c_char_p)
pyfunc = signature(_pyfunc)
pyfunc(my_str_buf)
print(my_str_buf.value)
该示例通过 ctypes api 在 python 函数中包装了一个可调用的 python c。 目标是向 python 函数传递一个指向 c 字符串的指针,让它修改它的内容(提供一个假值),然后返回给调用者。
我首先通过 ctypes 函数 create_string_buffer 创建一个可变字符串缓冲区。
从例子可以看出,字符串缓冲区确实是可变的。
之后,我使用 ctypes.CFUNCTYPE(ct.c_int, ct.c_char_p) 创建了一个 c 函数原型,然后使用我的 python 函数实例化该原型,该函数应该使用相同的签名来调用。最后我用我的可变字符串缓冲区调用 python 函数。
让我恼火的是,当函数被调用时,传递给该函数形状的参数从<class 'ctypes.c_char_Array_9'> 类型转变为<class 'bytes'>。不幸的是,原来的可变数据类型变成了一个完全没用的非可变字节对象。
这是一个 ctypes 错误吗? Python 版本是 3.6.6。
这是输出:
<class 'ctypes.c_char_Array_9'>
b'tes*1234'
b'4321test'
<class 'bytes'>
Traceback (most recent call last):
File "_ctypes/callbacks.c", line 234, in 'calling callback function'
File "C:/Users/andree/source/Python_Tests/ctypes_cchar_prototype.py", line 5, in _pyfunc
a_c_string.value = b"87654321"
AttributeError: 'bytes' object has no attribute 'value'
b'4321test'
预期输出:
<class 'ctypes.c_char_Array_9'>
b'tes*1234'
b'4321test'
<class 'ctypes.c_char_Array_9'>
b'87654321'
【问题讨论】:
-
如果将参数类型更改为
ct.c_char * 9会怎样?所以signature = ct.CFUNCTYPE(ct.c_int, ct.c_char * 9). -
我注意到它仍然不能反映您想要的确切行为。它按值传递
my_str_buf。如果你想在函数中改变my_str_buf,你可以通过引用传递它,比如pyfunc(ct.byref(my_str_buf))。这将要求您将参数类型更改为指向 char 数组的指针。signature = ct.CFUNCTYPE(ct.c_int, ct.POINTER(ct.c_char * 9))并在函数内修改该指针的内容。a_c_string.contents.value = b"87654321"总而言之,这应该会给你你想要的行为。 -
这也行。作为副作用,它需要我传递精确宽度的字符串。这可能是一个优势,也可能不是。就我而言,这很好。
标签: python-3.x string byte ctypes mutable