如何将 python 字符串写入 python 中的 C 结构 (char *) 成员?
How to write a python string to a C struct's (char *) member in python?
我有一个从 C 传递到 python 的 C 结构,我想更改 python 中的 (char *) 成员的内容。
little.c
#include <Python.h>
typedef struct little littleStruct;
struct little{
char *memberStr;
PyObject *(*callFunc)(littleStruct *s, char *funcName, PyObject *paraList);
};
littleStruct *createLittle();
void init_little(littleStruct *s);
PyObject *little_callFunc(littleStruct *s, char *funcName, PyObject *paraList);
littleStruct *createLittle(){
littleStruct *little = malloc(sizeof(littleStruct));
init_little(little);
return little;
}
void init_little(littleStruct *s){
s->memberStr = malloc(sizeof(char)*128);
memset(s->memberStr, 0, 128);
s->callFunc = little_callFunc;
}
PyObject *little_callFunc(littleStruct *s, char *funcName, PyObject *paraList){
PyGILState_STATE st = PyGILState_Ensure();
PyObject *module, *moduleDict, *moduleFunc, *moduleFuncArgs, *moduleFuncRet;
// skipped error checking in this little example
module = PyImport_ImportModule(funcName);
moduleDict = PyModule_GetDict(module);
moduleFunc = PyDict_GetItemString(moduleDict, funcName);
// set arguments for littleOperation.littleOperation
moduleFuncArgs = PyTuple_New(2);
PyTuple_SetItem(moduleFuncArgs, 0, PyLong_FromVoidPtr(s));
PyTuple_SetItem(moduleFuncArgs, 1, paraList);
printf("==== In C, before call func: %s, address(memberStr): %p, memberStr: %s\n", funcName, s->memberStr, s->memberStr);
moduleFuncRet = PyObject_CallObject(moduleFunc, moduleFuncArgs);
printf("==== In C, after call func: %s, address(memberStr): %p, memberStr: %s\n", funcName, s->memberStr, s->memberStr);
PyGILState_Release(st);
return moduleFuncRet;
}
编译成.so
文件
gcc -g -fPIC -c little.c -I/usr/include/python2.7 -L/usr/lib/python2.7 -lpython2.7
gcc -shared little.o -o little.so
mv little.so /usr/lib
创建结构并在little.py
中调用结构成员函数callFunc
little.py
from ctypes import *
class littleStruct(Structure):
pass
littleStruct._fields_ = [
("memberStr", c_char_p),
("callFunc", CFUNCTYPE(py_object, POINTER(littleStruct), c_char_p, py_object))
]
def main():
try:
littleDll = PyDLL("little.so")
littleDll.createLittle.restype = POINTER(littleStruct)
little = littleDll.createLittle()
paraList = {"para0": 0, "para1": "test"}
ret = little.contents.callFunc(little, "littleOperation", paraList)
except Exception, e:
print("%s"%str(e))
if __name__ == "__main__":
main()
littleOperation.py
from little import littleStruct
from ctypes import memset
def littleOperation(little_pointer, paraList):
try:
little = littleStruct.from_address(little_pointer)
# set little.memberStr
# little.memberStr = paraList["para1"]
for i in range(0, len(paraList["para1"])):
memset(id(little.memberStr)+i, ord(paraList["para1"][i]), 1)
print("After set little.memberStr in littleOperation.py")
return {"status": 0}
except Exception, e:
print("%s"%str(e))
return {"status": -1}
如果我直接将python字符串赋值给C struct(char*)成员,little.memberStr = paraList["para1"]
,地址将会改变。
==== In C, before call func: littleOperation, address(memberStr): 0x226a200, memberStr:
After set little.memberStr in littleOperation.py
==== In C, after call func: littleOperation, address(memberStr): 0x7fb9db196ec4, memberStr: test
我试了ctypes.memset
,地址还是一样,但是(char *)成员一点都没变。
==== In C, before call func: littleOperation, address(memberStr): 0x1d97200, memberStr:
After set little.memberStr in littleOperation.py
==== In C, after call func: littleOperation, address(memberStr): 0x1d97200, memberStr:
为什么 ctypes.memset
不起作用?
由于 ctypes
对 c_char_p
类型的特殊处理,它 returns 一个 Python
访问时的字节字符串对象。您的 memset/id
代码正在修改表示该不可变字节字符串对象的 PyObject 结构的前几个字节。
相反,将 littleStruct._fields_
中的 memberStr
类型更改为 POINTER(c_char)
以保留对 C 指针的访问。
然后,在 littleOperation.py
中,您可以将 POINTER(c_char)
转换为 POINTER(c_char * <size>
,其中 <size>
是所需的写入大小或缓冲区的已知大小。然后你可以通过 .contents
取消引用指针来访问一个固定大小的数组,该数组可以直接通过 .value
:
分配一个字节字符串
p = cast(little.memberStr,POINTER(c_char * 128))
p.contents.value = paraList['para1']
请参阅 another answer of mine,其中有一个更小的独立示例。
我有一个从 C 传递到 python 的 C 结构,我想更改 python 中的 (char *) 成员的内容。
little.c
#include <Python.h>
typedef struct little littleStruct;
struct little{
char *memberStr;
PyObject *(*callFunc)(littleStruct *s, char *funcName, PyObject *paraList);
};
littleStruct *createLittle();
void init_little(littleStruct *s);
PyObject *little_callFunc(littleStruct *s, char *funcName, PyObject *paraList);
littleStruct *createLittle(){
littleStruct *little = malloc(sizeof(littleStruct));
init_little(little);
return little;
}
void init_little(littleStruct *s){
s->memberStr = malloc(sizeof(char)*128);
memset(s->memberStr, 0, 128);
s->callFunc = little_callFunc;
}
PyObject *little_callFunc(littleStruct *s, char *funcName, PyObject *paraList){
PyGILState_STATE st = PyGILState_Ensure();
PyObject *module, *moduleDict, *moduleFunc, *moduleFuncArgs, *moduleFuncRet;
// skipped error checking in this little example
module = PyImport_ImportModule(funcName);
moduleDict = PyModule_GetDict(module);
moduleFunc = PyDict_GetItemString(moduleDict, funcName);
// set arguments for littleOperation.littleOperation
moduleFuncArgs = PyTuple_New(2);
PyTuple_SetItem(moduleFuncArgs, 0, PyLong_FromVoidPtr(s));
PyTuple_SetItem(moduleFuncArgs, 1, paraList);
printf("==== In C, before call func: %s, address(memberStr): %p, memberStr: %s\n", funcName, s->memberStr, s->memberStr);
moduleFuncRet = PyObject_CallObject(moduleFunc, moduleFuncArgs);
printf("==== In C, after call func: %s, address(memberStr): %p, memberStr: %s\n", funcName, s->memberStr, s->memberStr);
PyGILState_Release(st);
return moduleFuncRet;
}
编译成.so
文件
gcc -g -fPIC -c little.c -I/usr/include/python2.7 -L/usr/lib/python2.7 -lpython2.7
gcc -shared little.o -o little.so
mv little.so /usr/lib
创建结构并在little.py
callFunc
little.py
from ctypes import *
class littleStruct(Structure):
pass
littleStruct._fields_ = [
("memberStr", c_char_p),
("callFunc", CFUNCTYPE(py_object, POINTER(littleStruct), c_char_p, py_object))
]
def main():
try:
littleDll = PyDLL("little.so")
littleDll.createLittle.restype = POINTER(littleStruct)
little = littleDll.createLittle()
paraList = {"para0": 0, "para1": "test"}
ret = little.contents.callFunc(little, "littleOperation", paraList)
except Exception, e:
print("%s"%str(e))
if __name__ == "__main__":
main()
littleOperation.py
from little import littleStruct
from ctypes import memset
def littleOperation(little_pointer, paraList):
try:
little = littleStruct.from_address(little_pointer)
# set little.memberStr
# little.memberStr = paraList["para1"]
for i in range(0, len(paraList["para1"])):
memset(id(little.memberStr)+i, ord(paraList["para1"][i]), 1)
print("After set little.memberStr in littleOperation.py")
return {"status": 0}
except Exception, e:
print("%s"%str(e))
return {"status": -1}
如果我直接将python字符串赋值给C struct(char*)成员,little.memberStr = paraList["para1"]
,地址将会改变。
==== In C, before call func: littleOperation, address(memberStr): 0x226a200, memberStr:
After set little.memberStr in littleOperation.py
==== In C, after call func: littleOperation, address(memberStr): 0x7fb9db196ec4, memberStr: test
我试了ctypes.memset
,地址还是一样,但是(char *)成员一点都没变。
==== In C, before call func: littleOperation, address(memberStr): 0x1d97200, memberStr:
After set little.memberStr in littleOperation.py
==== In C, after call func: littleOperation, address(memberStr): 0x1d97200, memberStr:
为什么 ctypes.memset
不起作用?
由于 ctypes
对 c_char_p
类型的特殊处理,它 returns 一个 Python
访问时的字节字符串对象。您的 memset/id
代码正在修改表示该不可变字节字符串对象的 PyObject 结构的前几个字节。
相反,将 littleStruct._fields_
中的 memberStr
类型更改为 POINTER(c_char)
以保留对 C 指针的访问。
然后,在 littleOperation.py
中,您可以将 POINTER(c_char)
转换为 POINTER(c_char * <size>
,其中 <size>
是所需的写入大小或缓冲区的已知大小。然后你可以通过 .contents
取消引用指针来访问一个固定大小的数组,该数组可以直接通过 .value
:
p = cast(little.memberStr,POINTER(c_char * 128))
p.contents.value = paraList['para1']
请参阅 another answer of mine,其中有一个更小的独立示例。