Python ctypes从c函数返回一个字符串

Python ctypes从c函数返回一个字符串,python,c,ctypes,language-binding,Python,C,Ctypes,Language Binding,我是Python的老手,但对C语言没有太多涉猎。在互联网上半天没有找到适合我的东西后,我想我会在这里寻求帮助 我想做的是编写一个简单的C函数,它接受一个字符串并返回一个不同的字符串。我计划用几种语言(Java、Obj-C、Python等)绑定这个函数,所以我认为它必须是纯C语言 这是我到目前为止所拥有的。注意,在Python中尝试检索值时,我得到了一个segfault 你好,c 我已经读到segfault是由C释放最初分配给返回字符串的内存引起的。也许我找错人了 实现我想要的目标的正确方法是什么

我是Python的老手,但对C语言没有太多涉猎。在互联网上半天没有找到适合我的东西后,我想我会在这里寻求帮助

我想做的是编写一个简单的C函数,它接受一个字符串并返回一个不同的字符串。我计划用几种语言(Java、Obj-C、Python等)绑定这个函数,所以我认为它必须是纯C语言

这是我到目前为止所拥有的。注意,在Python中尝试检索值时,我得到了一个segfault

你好,c 我已经读到segfault是由C释放最初分配给返回字符串的内存引起的。也许我找错人了


实现我想要的目标的正确方法是什么?

在hello.c中,返回一个本地数组。必须返回指向数组的指针,该数组必须使用malloc动态分配

char* hello(char* name)
{ 
    char hello[] = "Hello ";
    char excla[] = "!\n";
    char *greeting = malloc ( sizeof(char) * ( strlen(name) + strlen(hello) + strlen(excla) + 1 ) );
    if( greeting == NULL) exit(1);
    strcpy( greeting , hello);
    strcat(greeting, name);
    strcat(greeting, excla);
    return greeting;
}

下面是发生的情况。为什么它会断裂。调用hello()时,C堆栈指针向上移动,为函数所需的任何内存腾出空间。除了一些函数调用开销外,所有函数局部变量都在那里进行管理。因此,
静态字符问候语[100]
,意味着增加的堆栈的100字节用于该字符串。您可以使用一些操作该内存的函数。最后,在堆栈上放置指向问候语内存的指针。然后从调用返回,此时,堆栈指针缩回到调用前的原始位置。因此,在调用期间堆栈上的100个字节基本上可以再次获取,因为堆栈被进一步操作。包括指向该值并返回的地址字段。在这一点上,谁知道它会发生什么,但它很可能被设置为零或其他值。当你试图访问它,好像它仍然是可行的内存,你会得到一个错误


要想四处走动,你需要以不同的方式管理内存。您可以让您的函数
alloc
占用堆上的内存,但您需要确保稍后通过绑定将其
释放()。或者,您可以编写您的函数,以便绑定语言将要使用的内存传递给它。

您的问题是,在堆栈上分配了问候语,但当函数返回时,堆栈将被破坏。您可以动态分配内存:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

const char* hello(char* name) {
    char* greeting = malloc(100);
    snprintf("Hello, %s!\n", 100, name)
    printf("%s\n", greeting);
    return greeting;
}

我今天遇到了同样的问题,发现您必须通过在方法上设置
restype
来覆盖默认的返回类型(
int
)。请参见ctype文档中的退货类型


我也遇到了同样的问题,但使用了不同的方法。假设我在字符串列表中找到一个字符串,该字符串与某个值匹配

基本上,我使用列表中最长字符串的大小初始化了一个char数组。然后将其作为参数传递给我的函数以保存相应的值

#包括
#包括
#包括
void find_gline(字符**ganal_行,/*行数组*/
大小\u t大小,/*数组大小*/
char*idnb,/*id号用于检查*/
字符*resline){
/*迭代行并查找包含idnb的行
然后将结果影响到resline*/
对于(大小i=0;i
此函数由相应的python函数包装:


def find_gline_wrap(行:list,arg:str,cdll):
""
#设置参数类型
mlen=maxlen(行)#给出字符串列表中最长字符串的长度
linelen=len(行)
line\u array=ctypes.c\u char\u p*linelen
cdll.find_gline.argtypes=[
线阵,
ctypes.c_size_t,
ctypes.c_char_p,
ctypes.c_char_p,
]
#
argbyte=字节(arg,“utf-8”)
resbyte=bytes(“,“utf-8”)
ganal_线=线_阵列(*线)
大小=ctypes.c\u大小\u t(linelen)
idnb=ctypes.c_char_p(argbyte)
resline=ctypes.c\u char\p(resbyte*mlen)
pdb.set_trace()
结果=cdll.find_gline(ganal_线、大小、idnb、resline)
#最后去掉空字符
结果=resline.value[:-1]。解码(“utf-8”)
返回结果

您需要适当地设置
foo.restype
。是否确实要使用
静态
?不是线程安全的。在Python中分配内存并让C代码填充内容不是更好吗?或者在C代码中分配,并导出deallocator;使用
strdup
malloc
。但实际上,如果你想用C语言做这类事情,那就投资一本C语言书。C与Python等高级语言有很大不同。除了您描述的问题之外,您的缓冲区是静态的,因此所有调用只有一个缓冲区,因此下一个调用将更改第一个返回值所指向的位置。保持它的局部性而不是
静态
意味着当函数返回时,它的生存期结束,这使得它不合适。这还没有触及缓冲区溢出漏洞!呵呵,这里显然是个傻瓜如果我删除
static
gcc会给我一个警告。分配返回内存的正确方法是什么?我只是在寻找一些安全和直接的东西。C中几乎没有安全或直接的东西;-)至少如果你用Python的思维方式工作的话。读一本好的C语言书。阅读Stackoverflow上现有的问题和答案在紧要关头是可行的,但我不会打赌。(顺便说一句,gcc给出了一个警告,原因正是我所暗示的:这是错误的,您返回的是一个不再存在的地址。)非常好!非常感谢。这个答案的后续问题是:既然我们在这里分配内存,那么它在哪里/什么时候被释放?如果我
char* hello(char* name)
{ 
    char hello[] = "Hello ";
    char excla[] = "!\n";
    char *greeting = malloc ( sizeof(char) * ( strlen(name) + strlen(hello) + strlen(excla) + 1 ) );
    if( greeting == NULL) exit(1);
    strcpy( greeting , hello);
    strcat(greeting, name);
    strcat(greeting, excla);
    return greeting;
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

const char* hello(char* name) {
    char* greeting = malloc(100);
    snprintf("Hello, %s!\n", 100, name)
    printf("%s\n", greeting);
    return greeting;
}
import ctypes

# define the interface
hello = ctypes.cdll.LoadLibrary('./hello.so')
# find lib on linux or windows
libc = ctypes.CDLL(ctypes.util.find_library('c'))
# declare the functions we use
hello.hello.argtypes = (ctypes.c_char_p,)
hello.hello.restype = ctypes.c_char_p
libc.free.argtypes = (ctypes.c_void_p,)

# wrap hello to make sure the free is done
def hello(name):
    _result = hello.hello(name)
    result = _result.value
    libc.free(_result)
    return result

# do the deed
print hello("Frank")
import ctypes
hello = ctypes.cdll.LoadLibrary('./hello.so')
name = "Frank"
c_name = ctypes.c_char_p(name)
hello.hello.restype = ctypes.c_char_p # override the default return type (int)
foo = hello.hello(c_name)
print c_name.value
print ctypes.c_char_p(foo).value