Python使用ctypes传递char *数组并填充结果

时间:2013-05-22 19:09:45

标签: python c++ c ctypes

我正在尝试使用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中,我必须正确地设置指针数组或其他东西。它似乎到达循环中的第二个节点,然后出现问题,因为我已经看到将数据吐出到命令行。任何见解都会非常感激。

3 个答案:

答案 0 :(得分:10)

以下代码有效:

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

答案 1 :(得分:4)

在这一行:

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);

答案 2 :(得分:2)

我不能(轻松)测试代码,但根据你所说的,我的猜测就是问题在于......

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

根据create_string_buffer() ...

的Python文档
  

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

     

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

...这意味着它正在创建char[7],但strcpy()将尝试复制字符串的NULL终结符,因此如果您的最大“节点名称”长度为7如果您使用char[8]代替NULL,则可能需要memcpy(array[i], nodes[i], 7)才能保留strcpy()

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