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

Tha*_*all 12 c python ctypes language-binding

我是一名Python资深人士,但在C中没有涉足太多.在互联网上找不到任何适用于我的东西半天之后,我想我会问这里并得到我需要的帮助.

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

这是我到目前为止所拥有的.注意我在尝试在Python中检索值时遇到了段错误.

你好ç

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

const char* hello(char* name) {
    static char greeting[100] = "Hello, ";
    strcat(greeting, name);
    strcat(greeting, "!\n");
    printf("%s\n", greeting);
    return greeting;
}
Run Code Online (Sandbox Code Playgroud)

main.py

import ctypes
hello = ctypes.cdll.LoadLibrary('./hello.so')
name = "Frank"
c_name = ctypes.c_char_p(name)
foo = hello.hello(c_name)
print c_name.value # this comes back fine
print ctypes.c_char_p(foo).value # segfault
Run Code Online (Sandbox Code Playgroud)

我已经读过,segfault是由C释放最初为返回的字符串分配的内存引起的.也许我只是在咆哮错误的树?

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

tde*_*ney 16

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

但这只是战斗的一部分,因为现在你有内存泄漏.你可以用另一个ctypes调用free()来插入它.

...或者更好的方法是阅读官方C绑定到python(python 2.x在http://docs.python.org/2/c-api/和python 3.x在http:/ /docs.python.org/3/c-api/).让你的C函数创建一个python字符串对象并将其交回.它将被python自动收集垃圾.既然你正在编写C面,你就不必玩ctypes游戏了.

...编辑..

我没有编译和测试,但我认为这个.py会起作用:

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")
Run Code Online (Sandbox Code Playgroud)

  • 我不能做你推荐的"更好的方法"(返回一个Python对象)因为我需要用多种语言绑定这个函数. (3认同)

小智 6

n 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 非常好!谢谢.关于这个答案的后续问题:既然我们在这里分配内存,那么它何时/何​​时被解除分配?如果我把这个功能称为10k次,我会有一个可怕的泄漏吗? (2认同)
  • 我想我必须在Python绑定中释放它?或者我还能在哪里做到这一点? (2认同)

pba*_*tey 5

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

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
Run Code Online (Sandbox Code Playgroud)