【问题标题】:Python using ctypes to pass a char * array and populate resultsPython 使用 ctypes 传递一个 char * 数组并填充结果
【发布时间】:2013-05-17 23:29:48
【问题描述】:

我正在尝试使用 ctypes 在 python 中创建一个 char * 数组,以传递给用于填充字符串的库。我期望 4 个字符串返回,每个字符串的长度不超过 7 个字符。

我的py代码是这样的

testlib.py

from ctypes import *
primesmile = CDLL("/primesmile/lib.so")

getAllNodeNames = primesmile.getAllNodeNames
getAllNodeNames.argtypes = [POINTER(c_char_p)]
results = (c_char_p * 4)(addressof(create_string_buffer(7)))
err = getAllNodeNames(results)

lib.cpp

void getAllNodeNames(char **array){
    DSL_idArray nodes; //this object returns const char * when iterated over
    network.GetAllNodeIds(nodes);
    for(int i = 0; i < (nodes.NumItems()); i++){
    strcpy(array[i],nodes[i]);
    }
}

当我尝试运行此代码时,我不断收到分段错误。我已经从 C 创建了一个完美运行的测试,但在 Python 中我必须错误地设置指针数组或其他东西。它似乎到达了循环中的第二个节点,然后出现了一个问题,正如我从将数据吐出到命令行中看到的那样。任何见解将不胜感激。

【问题讨论】:

    标签: python c++ c ctypes


    【解决方案1】:

    以下代码有效:

    test.py:

    import ctypes
    lib = ctypes.CDLL("./libtest.so")
    string_buffers = [ctypes.create_string_buffer(8) for i in range(4)]
    pointers = (ctypes.c_char_p*4)(*map(ctypes.addressof, string_buffers))
    lib.test(pointers)
    results = [s.value for s in string_buffers]
    print results
    

    test.c(使用gcc test.c -o libtest.so -shared -fPIC编译成libtest.so):

    #include <string.h>
    void test(char **strings) {
        strcpy(strings[0],"this");
        strcpy(strings[1],"is");
        strcpy(strings[2],"a");
        strcpy(strings[3],"test!");
    }
    

    正如 Aya 所说,您应该确保为终止零留出空间。但我认为您的主要问题是字符串缓冲区被垃圾收集或类似的东西,因为不再直接引用它。或者当没有为字符串缓冲区存储引用时,其他东西会在字符串缓冲区的创建过程中造成麻烦。例如,这会导致四次相同的地址而不是不同的地址:

    import ctypes
    pointers = [ctypes.addressof(ctypes.create_string_buffer(8)) for i in range(4)]
    print pointers
    

    【讨论】:

    • 您还可以为缓冲区创建一个二维数组:string_buffers = ((ctypes.c_char * 8) * 4)()。当您在下一行定义 pointers 时,它会进行相同的迭代。
    • 我们不必在 C 端 extern "C" 吗?
    • @GyuHyeonChoi extern"C" 只需要告诉 C++ 创建与 C 兼容的符号(参见 C++ 名称修改)。
    【解决方案2】:

    在这一行:

    results = (c_char_p * 4)(addressof(create_string_buffer(7)))
    

    您正在创建一个 7 个字节的缓冲区,然后尝试使用它来保存 4 个字符指针(每个可能是 4 个字节),然后还将 4 个 8 字节字符串复制到它可能发生的随机地址中指向。您需要为每个字符串分配一个缓冲区,并分配指针数组。像这样的:

    s = []
    for i in range(4):
        s[i] = create_string_buffer(8)
    results = (c_char_p * 4)(s);
    

    【讨论】:

      【解决方案3】:

      我不能(轻松)测试代码,但根据你所说的,我的猜测是问题出在这条线上......

      results = (c_char_p * 4)(addressof(create_string_buffer(7)))
      

      根据create_string_buffer() 的 Python 文档...

      init_or_size 必须是指定数组大小的整数, 或用于初始化数组项的字符串。

      如果将字符串指定为第一个参数,则缓冲区为一个 item 大于字符串的长度,因此最后一个元素 该数组是一个 NUL 终止字符。整数可以作为 第二个参数允许指定数组的大小,如果 不应使用字符串的长度。

      ...这意味着它正在创建一个char[7],但strcpy() 将尝试复制一个字符串的NULL 终止符,因此如果您的最大“节点名称”长度为七个字符,则您需要一个char[8] 来保存NULL,尽管您可能会使用memcpy(array[i], nodes[i], 7) 而不是strcpy()。

      无论哪种方式,改用create_string_buffer(8) 可能是最安全的。

      【讨论】:

        猜你喜欢
        • 2014-01-28
        • 2014-12-04
        • 1970-01-01
        • 2018-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-23
        相关资源
        最近更新 更多