簡體   English   中英

如何將 python 字符串寫入 python 中 C 結構的 (char *) 成員?

[英]How to write a python string to a C struct's (char *) member in python?

我有一個 C 結構從 C 傳遞到 python,我想更改 C 的內容,我想更改 C 的內容,並且我想更改 C 的內容

小.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中創建結構並調用結構成員 function callFunc

小.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()

小操作.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不起作用?

由於ctypesc_char_p類型的特殊處理,訪問時返回一個 Python 字節串 object。 您的memset/id代碼正在修改 PyObject 結構的前幾個字節,表示該不可變字節字符串 object。

相反,將 littleStruct._fields_ 中的memberStr類型littleStruct._fields_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']

請參閱我的另一個答案,它有一個較小的獨立示例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM