繁体   English   中英

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

[英]ctypes return a string from c function

我是一名 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 释放最初为返回的字符串分配的内存引起的。 也许我只是在吠错树?

完成我想要的东西的正确方法是什么?

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

#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(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")

在 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;
}

我今天遇到了同样的问题,发现您必须通过在方法上设置restype来覆盖默认返回类型 ( int )。 请参阅此处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

这就是发生的事情。 以及它为什么会坏。 调用 hello() 时,C 堆栈指针上移,为函数所需的任何内存腾出空间。 除了一些函数调用开销外,所有本地函数都在那里进行管理。 所以static char greeting[100]意味着增加的堆栈的 100 个字节用于该字符串。 您可以使用一些操作该内存的函数。 在你将一个指向问候内存的指针放在堆栈上。 然后你从调用中返回,此时堆栈指针被收回到调用之前的原始位置。 因此,在您调用期间位于堆栈上的 100 个字节基本上可以在进一步操作堆栈时再次使用。 包括指向该值和您返回的地址字段。 那时,谁知道它会发生什么,但它很可能被设置为零或其他值。 当你试图访问它时,就好像它仍然是可用的内存一样,你会得到一个段错误。

为了解决这个问题,您需要以某种方式以不同的方式管理该内存。 你可以有你的功能alloc吃了堆内存,但你必须确保它得到free() “版在以后的日子,你的绑定。 或者,您可以编写您的函数,以便绑定语言将大量内存传递给它以供使用。

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

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

#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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM