Вопрос по python, ctypes, c, language-binding – ctypes возвращают строку из функции c

11

я ветеран Python, но убежищеЯ много баловался с C. После полдня, когда я не нашел в интернете ничего подходящего для меня, я подумал, что попрошу здесь и получу необходимую помощь.

Я хочу написать простую функцию C, которая принимает строку и возвращает другую строку. Я планирую связать эту функцию на нескольких языках (Java, Obj-C, Python и т. Д.), Поэтому я думаю, что это должен быть чистый C?

Вот'Это то, что я имею до сих пор. Заметьте, что я получаю segfault при попытке получить значение в Python.

Привет
#include 
#include 
#include 

const char* hello(char* name) {
    static char greeting[100] = "Hello, ";
    strcat(greeting, name);
    strcat(greeting, "!\n");
    printf("%s\n", greeting);
    return greeting;
}
main.pyI»
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 освобождает память, которая была первоначально выделена для возвращаемой строки. Может я'я просто лаю не на том дереве?

Какие'правильный способ сделать то, что я хочу?

Помимо описанной проблемы, ваш буферstatic, так что's только один для всех вызовов, поэтому следующий вызов изменит то, на что указывает первое возвращаемое значение. Держать это локально, а неstatic означает, что его время жизни заканчивается, когда функция возвращается, что делает его неподходящим. Тот'даже не касаясь уязвимости переполнения буфера! user395760
Вам нужно установитьfoo.restype соответственно. Вы действительно хотите использоватьstatic? Не потокобезопасен. Wouldn»Вам лучше выделить память в Python и позволить коду C заполнять его содержимым? Или разместите в коде C, и экспортируйте также. David Heffernan
Вы, вероятно, должны вернуть копию строки; использованиеstrdup или жеmalloc для этого. Но на самом деле, если вы хотите заниматься такими вещами на C, тогда вкладывайте деньги в C-книгу. C довольно сильно отличается от языков более высокого уровня, таких как Python. Fred Foo
Хех, очевидно, C Noob здесь. :) если я уберуstatic GCC дает мне предупреждение. Какие'правильный способ выделить память для возврата? Я'Я просто ищу что-то безопасное и простое. Thane Brimhall
Существует мало безопасных или простых в C ;-) По крайней мере, если вы работаете с мышлением Python. Прочитайте хорошую C книгу. чтениесуществующий вопросы и ответы здесь на Stackoverflow работает в крайнем случае, но я бы не сталСтавлю на это. (Кстати, GCC дает предупреждение по той самой причине, на которую я намекнул:неправильно, тывернуть адрес чего-то, что небольше не существует.) user395760

Ваш Ответ

3   ответа
2

что случилось? И почему этоломается Когда вызывается hello (), указатель стека C перемещается вверх, освобождая место для любой памяти, необходимой вашей функции. Наряду с некоторыми накладными расходами вызова функций, все ваши локальные функции управляются там. Чтобыstatic char greeting[100], означает, что 100 байтов увеличенного стека предназначены для этой строки. Вы чем используете некоторые функции, которые манипулируют этой памятью. В это место вы помещаете указатель в стек на память приветствия. И затем вы возвращаетесь из вызова, после чего указатель стека возвращается к нему 'с оригинальной позиции до вызова. Таким образом, те 100 байтов, которые были в стеке на время вашего вызова, по сути, снова пригодятся для захвата, так как стеком в дальнейшем манипулируют. Включая поле адреса, которое указывало на это значение и которое вы вернули. В тот момент, кто знает, что с ним происходит, но этоСкорее всего, установлен на ноль или другое значение. И когда вы пытаетесь получить к нему доступ, как будто это все еще жизнеспособная память, вы получаете ошибку сегмента.

Чтобы обойти, нужно как-то по-другому управлять этой памятью. Вы можете иметь свою функциюallocсъел память в куче, но тыЯ должен убедиться, что он получитfree()на более поздний срок, по вашей привязке. ИЛИ, вы можете написать свою функцию так, чтобы язык связывания передавал ей кусочек памяти для использования.

free(pointer), Тот'с противоположностьюmalloc и друзья. Вы'Я должен предоставить привязку для этого или надеяться, что она у вас уже есть, у большинства языков, связанных с C, есть какой-то механизм для этого. Travis Griggs
Отличное объяснение того, как все это работает. Как бы я освободил память, когда использовал результат в моем связывании? Thane Brimhall
14

что приветствие было размещено в стеке, но стек уничтожается, когда функция возвращается. Вы можете выделить память динамически:

#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;
}
</string.h></stdio.h></stdlib.h>

Но это'Это только часть битвы, потому что теперь у вас утечка памяти. Вы можете подключить это с помощью другого вызова 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.

...редактировать..

Я не't скомпилировать и протестировать, но я думаю, что .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")
Я могу'т "гораздо лучше подходить Вы рекомендовали (вернуть объект Python), потому что мне нужно связать эту функцию на нескольких языках. Thane Brimhall
Хорошо, это может быть проблемой! Другой вариант - SWIG, который может связывать несколько языков (swig.org/compat.html#SupportedLanguages). Время от времени я использую ctypes, но он может быть громоздким, когда интерфейс сложный. tdelaney
Я добавил код Python в пример - он не проверен, но выглядит правильно для меня (смеется). tdelaney
6

а массив, который должен быть динамически объявлен с использованием 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;
}
Код должен также проверять наличие сбоев malloc и правильно с ним справляться. Warren Weckesser
Очень хорошо! Спасибо. Дополнительный вопрос по этому ответу: так как мыперераспределение памяти здесь, где / когда она освобождается? Если я вызову эту функцию 10k раз, у меня будет ужасная утечка? Thane Brimhall
Ударь меня к этому. Это должно сделать работу (если нет каких-либо других непредвиденных проблем). Причина, по которой это неДо этого работало то, что, создавая строку, как вы, она была размещена в стеке и, таким образом, была потеряна после выхода из функции. Вместо этого решение использует malloc для размещения в куче и возвращает Python место, где найти строку. Geesh_SO
Я неЯ не знаю Python, но в соответствии с правилами, установленными вашим вопросом, вам придется создать функцию c и передать на нее указатель char *. user1944441

Похожие вопросы