简体   繁体   English

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

[英]ctypes return a string from c function

I'm a Python veteran, but haven't dabbled much in C. After half a day of not finding anything on the internet that works for me, I thought I would ask here and get the help I need.我是一名 Python 老手,但对 C 的涉猎不多。在互联网上半天没有找到适合我的任何东西后,我想我会在这里询问并获得我需要的帮助。

What I want to do is write a simple C function that accepts a string and returns a different string.我想要做的是编写一个简单的 C 函数,它接受一个字符串并返回一个不同的字符串。 I plan to bind this function in several languages (Java, Obj-C, Python, etc.) so I think it has to be pure C?我打算用几种语言(Java、Obj-C、Python 等)绑定这个函数,所以我认为它必须是纯 C?

Here's what I have so far.这是我到目前为止所拥有的。 Notice I get a segfault when trying to retrieve the value in Python.请注意,我在尝试检索 Python 中的值时遇到了段错误。

hello.c你好ç

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

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

I've read that the segfault is caused by C releasing the memory that was initially allocated for the returned string.我读过段错误是由 C 释放最初为返回的字符串分配的内存引起的。 Maybe I'm just barking up the wrong tree?也许我只是在吠错树?

What's the proper way to accomplish what I want?完成我想要的东西的正确方法是什么?

Your problem is that greeting was allocated on the stack, but the stack is destroyed when the function returns.您的问题是问候语是在堆栈上分配的,但是当函数返回时堆栈被销毁。 You could allocate the memory dynamically:您可以动态分配内存:

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

But that's only part of the battle because now you have a memory leak.但这只是战斗的一部分,因为现在您有内存泄漏。 You could plug that with another ctypes call to free().你可以用另一个 ctypes 调用 free() 来插入它。

...or a much better approach is to read up on the official C binding to python (python 2.x at http://docs.python.org/2/c-api/ and python 3.x at http://docs.python.org/3/c-api/ ). ...或者更好的方法是对在官方C语言绑定到Python(Python的2.X读了http://docs.python.org/2/c-api/在和Python 3.x的HTTP:/ /docs.python.org/3/c-api/ )。 Have your C function create a python string object and hand that back.让您的 C 函数创建一个 python 字符串对象并将其交还。 It will be garbage collected by python automatically.它将被python自动垃圾收集。 Since you are writing the C side, you don't have to play the ctypes game.既然是写C面,就不用玩ctypes的游戏了。

...edit.. ...编辑..

I didn't compile and test, but I think this .py would work:我没有编译和测试,但我认为这个 .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")

In hello.c you return a local array.在 hello.c 中,您返回一个本地数组。 You have to return a pointer to an array, which has to be dynamically allocated using malloc.您必须返回一个指向数组的指针,该数组必须使用 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;
}

I ran into this same problem today and found you must override the default return type ( int ) by setting restype on the method.我今天遇到了同样的问题,发现您必须通过在方法上设置restype来覆盖默认返回类型 ( int )。 See Return types in the ctype doc here .请参阅此处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

Here's what happens.这就是发生的事情。 And why it's breaking.以及它为什么会坏。 When hello() is called, the C stack pointer is moved up, making room for any memory needed by your function.调用 hello() 时,C 堆栈指针上移,为函数所需的任何内存腾出空间。 Along with some function call overhead, all of your function locals are managed there.除了一些函数调用开销外,所有本地函数都在那里进行管理。 So that static char greeting[100] , means that 100 bytes of the increased stack are for that string.所以static char greeting[100]意味着增加的堆栈的 100 个字节用于该字符串。 You than use some functions that manipulate that memory.您可以使用一些操作该内存的函数。 At the you place a pointer on the stack to the greeting memory.在你将一个指向问候内存的指针放在堆栈上。 And then you return from the call, at which point, the stack pointer is retracted back to it's original before call position.然后你从调用中返回,此时堆栈指针被收回到调用之前的原始位置。 So those 100 bytes that were on the stack for the duration of your call, are essentially up for grabs again as the stack is further manipulated.因此,在您调用期间位于堆栈上的 100 个字节基本上可以在进一步操作堆栈时再次使用。 Including the address field which pointed to that value and that you returned.包括指向该值和您返回的地址字段。 At that point, who knows what happens to it, but it's likely set to zero or some other value.那时,谁知道它会发生什么,但它很可能被设置为零或其他值。 And when you try to access it as if it were still viable memory, you get a segfault.当你试图访问它时,就好像它仍然是可用的内存一样,你会得到一个段错误。

To get around, you need to manage that memory differently somehow.为了解决这个问题,您需要以某种方式以不同的方式管理该内存。 You can have your function alloc ate the memory on the heap, but you'll need to make sure it gets free() 'ed at a later date, by your binding.你可以有你的功能alloc吃了堆内存,但你必须确保它得到free() “版在以后的日子,你的绑定。 OR, you can write your function so that the binding language passes it a glump of memory to be used.或者,您可以编写您的函数,以便绑定语言将大量内存传递给它以供使用。

I also ran into the same problem but used a different approach.我也遇到了同样的问题,但使用了不同的方法。 I was suppose to find a string in a list of strings matchin a certain value.我想在匹配某个值的字符串列表中找到一个字符串。

Basically I initalized a char array with the size of longest string in my list.基本上,我用列表中最长字符串的大小初始化了一个字符数组。 Then passed that as an argument to my function to hold the corresponding value.然后将其作为参数传递给我的函数以保存相应的值。

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

This function was wrapped by the corresponding python function:这个函数被对应的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