16

私はPythonのベテランですが、Cにあまり手を出していないのです。インターネット上で自分に合ったものが見つからなかった半日後、ここで質問して必要なサポートを受けたいと思いました。

私がやりたいのは、文字列を受け入れて別の文字列を返す単純なC関数を作成することです。この関数をいくつかの言語(Java、Obj-C、Pythonなど)でバインドする予定なので、純粋なCでなければならないと思いますか?

これが私がこれまでに持っているものです。Pythonで値を取得しようとすると、セグメンテーション違反が発生することに注意してください。

こんにちは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

セグメンテーション違反は、Cが返された文字列に最初に割り当てられたメモリを解放することによって引き起こされることを読みました。たぶん私は間違った木を吠えているだけですか?

私が望むことを達成するための適切な方法は何ですか?

4

5 に答える 5

18

問題は、グリーティングがスタックに割り当てられたが、関数が戻るとスタックが破棄されることです。メモリを動的に割り当てることができます。

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

しかし、メモリリークが発生したため、これは戦いの一部にすぎません。free()への別のctypes呼び出しでそれをプラグインすることができます。

...または、Pythonへの公式Cバインディング( http://docs.python.org/2/c-api/のpython 2.xおよびhttp:/のpython 3.x)を確認することをお勧めします。 /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")
于 2013-02-14T21:08:02.527 に答える
6

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;
}
于 2013-02-14T20:58:15.893 に答える
4

今日、これと同じ問題が発生しました。メソッドintを設定して、デフォルトのリターンタイプ()をオーバーライドする必要があることがわかりました。こちらのctypeドキュメントのリターンタイプrestypeを参照してください。

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
于 2019-05-13T17:40:56.620 に答える
2

これが何が起こるかです。そして、なぜそれが壊れているのか。hello()が呼び出されると、Cスタックポインタが上に移動し、関数に必要なメモリ用のスペースが確保されます。いくつかの関数呼び出しのオーバーヘッドに加えて、すべての関数ローカルがそこで管理されます。となることによってstatic char greeting[100]、は、増加したスタックの100バイトがその文字列用であることを意味します。そのメモリを操作するいくつかの関数を使用するよりも。で、グリーティングメモリへのポインタをスタックに配置します。次に、呼び出しから戻ります。その時点で、スタックポインターは、呼び出し位置の前の元の位置に戻ります。したがって、呼び出し中にスタック上にあった100バイトは、スタックがさらに操作されると、基本的に再び取得できるようになります。その値を指し、あなたが返したアドレスフィールドを含みます。その時点で、誰がそれに何が起こるかを知っていますが、ゼロまたは他の値に設定されている可能性があります。そして、それがまだ実行可能なメモリであるかのようにアクセスしようとすると、セグメンテーション違反が発生します。

回避するには、そのメモリを何らかの方法で別の方法で管理する必要があります。関数にヒープ上のメモリを食べさせることができますが、後日、バインディングによってallocそれが確実に処理されるようにする必要があります。free()または、バインディング言語が使用するメモリの塊を渡すように関数を作成することもできます。

于 2013-02-14T21:00:44.340 に答える
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
于 2020-01-03T03:40:09.000 に答える