使用ctypes将字符串从Python传递到C共享库的问题

时间:2016-07-01 18:39:07

标签: python c ctypes

我很难通过ctypes将字符串从Python传递到C:

我的C代码(编译成hello.so)

...
typedef struct test{
    unsigned int    a;
    unsigned char*  b;
} my_struct;

int hello(my_struct *in) {
    FILE *fp;
    ...
    fprintf(fp, "%d\t%s\n", in->a, in->b);
    fclose(fp);
    return 0;
}

我的Python代码:

...
from ctypes import *
...
class TEST_STRUCT(Structure):
    _fields_ = [("a", c_int),
                ("b", c_char_p)]
...
    hello_lib = ctypes.cdll.LoadLibrary("hello.so")
    hello = hello_lib.hello
    hello.argtypes = [POINTER(TEST_STRUCT)]
    name = create_string_buffer(b"test")
    hello_args = TEST_STRUCT(1, name)
    hello(ctypes.byref(hello_args))
...

我收到错误:     hello_args = TEST_STRUCT(1,name) TypeError:期望的字符串,找到c_char_Array_5

我试图将c_char_p更改为c_wchar_p或c_char * 5或c_wchar * 5等。有时它可以正常运行,结构的第一个int参数可以正确打印,但不是第二个字符串指针,我能做到最好get只是第一个字符't'而不是整个单词“test”。

BTW,我的python3版本是3.3.0

1 个答案:

答案 0 :(得分:1)

解决方案取决于您是否需要数据类型是可变的(从Python的角度来看)。

如果不需要数据是可变的,那么请不要打扰缓冲区构造函数。只需将bytes对象直接传递给TEST_STRUCT构造函数即可。例如

from ctypes import *

class TEST_STRUCT(Structure):
    _fields_ = [("a", c_int),
                ("b", c_char_p)]

hello_args = TEST_STRUCT(1, b"test")

如果您需要可变缓冲区,则需要在char*类中略微指定TEST_STRUCT类型。例如

from ctypes import *

class TEST_STRUCT(Structure):
    _fields_ = [("a", c_int),
                ("b", POINTER(c_char))] # rather than c_char_p

name = create_string_buffer(b"test")
hello_args = TEST_STRUCT(1, name)

c_char_pPOINTER(c_char)

表面上看,虽然这两者看起来很相似但却略有不同。 POINTER(c_char)适用于char数据的任何缓冲区。 c_char_p专门用于NULL终止字符串。作为此条件的副作用,所有c_char_p对象都是不可变的(在python端),以便使此保证更容易实施。例如。 {65, 66, 67, 0}是一个字符串("ABC"),而{1, 2, 3}则不是。