我是一名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;
}
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
我读过段错误是由C释放最初为返回的字符串分配的内存引起的。也许我只是在咆哮错误的树?
实现我想要的正确方法是什么?
答案 0 :(得分:15)
你的问题是问候是在堆栈上分配的,但是当函数返回时堆栈被销毁。您可以动态分配内存:
#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;
}
但这只是战斗的一部分,因为现在你有内存泄漏。你可以用另一个ctypes调用free()来插入它。
...或者更好的方法是阅读官方C绑定到python(http://docs.python.org/2/c-api/处的python 2.x和http://docs.python.org/3/c-api/处的python 3.x)。让你的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")
答案 1 :(得分: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;
}
答案 2 :(得分:2)
这是发生了什么。为什么它会破裂。调用hello()时,C堆栈指针向上移动,为函数所需的任何内存腾出空间。除了一些函数调用开销,所有函数本地都在那里进行管理。因此static char greeting[100]
表示增加的堆栈的100个字节用于该字符串。您可以使用一些操作该内存的函数。在你把一个指针放在堆栈上的问候记忆中。然后你从通话中返回,此时,堆栈指针缩回到它在呼叫位置之前的原始状态。因此,在调用期间堆栈上的那100个字节,在堆栈被进一步操作时,基本上可以再次抓取。包括指向该值和您返回的地址字段。那时,谁知道它发生了什么,但它可能设置为零或其他值。当你试图访问它时,就像它仍然是可行的内存,你会得到一个段错误。
为了解决这个问题,你需要以某种方式管理不同的内存。您可以让函数alloc
在堆上占用内存,但是您需要确保它在以后通过绑定获得free()
。或者,您可以编写您的函数,以便绑定语言将其传递给要使用的内存组合。
答案 3 :(得分:1)
我今天遇到了同样的问题,发现您必须通过在方法上设置int
来覆盖默认的返回类型(restype
)。请参阅ctype文档here中的返回类型。
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
答案 4 :(得分:1)
我也遇到了同样的问题,但是使用了不同的方法。我本来是想在某个匹配的字符串列表中找到一个字符串。
基本上,我初始化了一个char数组,其长度为列表中最长的字符串。然后将其作为参数传递给我的函数以保存相应的值。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void find_gline(char **ganal_lines, /*line array*/
size_t size, /*array size*/
char *idnb, /* id number for check */
char *resline) {
/*Iterates over lines and finds the one that contains idnb
then affects the result to the resline*/
for (size_t i = 0; i < size; i++) {
char *line = ganal_lines[i];
if (strstr(line, idnb) != NULL) {
size_t llen = strlen(line);
for (size_t k = 0; k < llen; k++) {
resline[k] = line[k];
}
return;
}
}
return;
}
此函数由相应的python函数包装:
def find_gline_wrap(lines: list, arg: str, cdll):
""
# set arg types
mlen = maxlen(lines) # gives the length of the longest string in string list
linelen = len(lines)
line_array = ctypes.c_char_p * linelen
cdll.find_gline.argtypes = [
line_array,
ctypes.c_size_t,
ctypes.c_char_p,
ctypes.c_char_p,
]
#
argbyte = bytes(arg, "utf-8")
resbyte = bytes("", "utf-8")
ganal_lines = line_array(*lines)
size = ctypes.c_size_t(linelen)
idnb = ctypes.c_char_p(argbyte)
resline = ctypes.c_char_p(resbyte * mlen)
pdb.set_trace()
result = cdll.find_gline(ganal_lines, size, idnb, resline)
# getting rid of null char at the end
result = resline.value[:-1].decode("utf-8")
return result