Python 3.x PythonCallable上奇怪的ctypes行为用c_char_p argtype包装c callable
在以下测试程序中,我观察到一种奇怪的ctypes相关行为:Python 3.x PythonCallable上奇怪的ctypes行为用c_char_p argtype包装c callable,python-3.x,string,byte,ctypes,mutable,Python 3.x,String,Byte,Ctypes,Mutable,在以下测试程序中,我观察到一种奇怪的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))
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)
该示例通过cTypesAPI将可调用的python c封装在python函数中。
目标是向python函数传递一个指向c字符串的指针,让它修改它的内容(提供一个假值),然后返回给调用方
我首先通过ctypes函数创建一个可变字符串缓冲区create\u string\u buffer
。
从示例中可以看出,字符串缓冲区确实是可变的
之后,我使用ctypes.CFUNCTYPE(ct.c_int,ct.c_char_p)
创建了一个c函数原型,然后用我的python函数实例化该原型,该函数应该使用相同的签名调用。最后,我使用可变字符串缓冲区调用python函数
令我恼火的是,调用函数时,传递给该函数形状的参数从
类型变为
。不幸的是,原来的可变数据类型变成了一个完全无用的非可变字节对象
这是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'
b'tes*1234'
b'4321测试'
回溯(最近一次呼叫最后一次):
文件“\u ctypes/callbacks.c”,第234行,“调用回调函数”
文件“C:/Users/andree/source/Python_Tests/ctypes_cchar_prototype.py”,第5行,在_pyfunc中
a_c_string.value=b“87654321”
AttributeError:“字节”对象没有属性“值”
b'4321测试'
预期产出:
<class 'ctypes.c_char_Array_9'>
b'tes*1234'
b'4321test'
<class 'ctypes.c_char_Array_9'>
b'87654321'
b'tes*1234'
b'4321测试'
b'87654321'
ctypes.c\u char\u p
自动转换为Python字节。如果不希望出现这种行为,请使用以下任一选项:
ctypes.POINTER(ctypes.c_char))
类PCHAR(ctypes.c\u char\p):传递
(派生抑制该行为)
请注意,LP_c_char
没有.value
属性,因此我必须直接取消引用指针以影响值的更改
另外,注意不要超过传入的可变缓冲区的长度。我添加了length
作为附加参数
例如:
将ctypes导入为ct
@CFUNCTYPE(ct.c_int,ct.POINTER(ct.c_char),ct.c_size\t)
def pyfunc(a_c_字符串,长度):
new_data=b'87654321\x00'#确保存在新的空终止。
如果len(新数据)>长度:#确保新数据不超过缓冲区长度
返回0#失败
对于枚举中的i,c(新的_数据):
a_c_字符串[i]=c
返回1#通过
my_str_buf=ct.创建字符串缓冲区(10)
结果=pyfunc(my_str_buf,len(my_str_buf))
打印(结果,我的字符串值)
my_str_buf=ct.创建字符串缓冲区(8)
结果=pyfunc(my_str_buf,len(my_str_buf))
打印(结果,我的字符串值)
1b'87654321'
0 b“
如果将参数类型更改为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))
。这将要求您将参数类型更改为指向字符数组的指针signature=ct.CFUNCTYPE(ct.c_int,ct.POINTER(ct.c_char*9))
并在函数中修改该指针的内容a_c_string.contents.value=b“87654321”
总之,这应该会给你想要的行为。这也很有效。作为一个副作用,它需要我传递精确宽度的字符串。这可能是一个优势,也可能不是。对我来说,这很好,这对我很有帮助。奇怪的是,如果我使用ct.CFUNCTYPE(ct.c_int,ct.POINTER(ct.c_char),ct.c_size_t)
与ct.CFUNCTYPE(ct.c_int,ct.c_char_p,ct.c_size_t)
。思想ct.c\u char\u p
仅仅是ct.POINTER(ct.c\u char)
@pqansc\u char\u p
的别名,对转换为Python字节对象有特殊处理。这不仅仅是一个别名,这也是我误解的根源。很高兴知道。@pqnasc\u void\u p
具有类似的特殊处理。它转换为PythonNone
或Pythonint
“请注意,LP_c_char没有.value属性,因此我必须直接[…]”->需要使用.contents.value
。