簡體   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