ctypes возвращают строку из функции c

я ветеран 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 освобождает память, которая была первоначально выделена для возвращаемой строки. Может я'я просто лаю не на том дереве?

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

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

Ответы на вопрос(3)

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

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

а массив, который должен быть динамически объявлен с использованием 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;
}
 Thane Brimhall14 февр. 2013 г., 22:05
Я полагаюДолжен ли он освободить его в привязке Python? Или где еще я это сделаю?
 user194444114 февр. 2013 г., 22:13
@delnan Конечно, это должно делать обе вещи, но я неНаписание идеальной функции, но пример того, как это сделать в первую очередь.
 user39576014 февр. 2013 г., 22:35
Выглядит хорошо для меня, по модулю стилистические проблемы, которые выходят за рамки придирки. Но я нене делайте это ежедневно, так что непринять это как гарантию. (И снова я за одинЯ думаю, что нулевая проверка стоит той строки кода, которая ей нужна. Но это субъективно.)
 user194444114 февр. 2013 г., 22:30
@delnan исправлено, пожалуйста, проверьте правильность.
 user194444114 февр. 2013 г., 22:05
Я неЯ не знаю Python, но в соответствии с правилами, установленными вашим вопросом, вам придется создать функцию c и передать на нее указатель char *.
 Geesh_SO14 февр. 2013 г., 22:02
Ударь меня к этому. Это должно сделать работу (если нет каких-либо других непредвиденных проблем). Причина, по которой это неДо этого работало то, что, создавая строку, как вы, она была размещена в стеке и, таким образом, была потеряна после выхода из функции. Вместо этого решение использует malloc для размещения в куче и возвращает Python место, где найти строку.
 Warren Weckesser14 февр. 2013 г., 22:11
Код должен также проверять наличие сбоев malloc и правильно с ним справляться.
 user194444114 февр. 2013 г., 22:03
@ThaneBrimhall Конечно, для каждого malloc вам нужно сделатьсвободно
 Thane Brimhall14 февр. 2013 г., 22:01
Очень хорошо! Спасибо. Дополнительный вопрос по этому ответу: так как мыперераспределение памяти здесь, где / когда она освобождается? Если я вызову эту функцию 10k раз, у меня будет ужасная утечка?
 user39576014 февр. 2013 г., 22:15
Тем не менее, это такая вопиющая проблема, и эти ошибки имеют такую ужасную историю, что я предпочел бы увидеть огромное предупреждение (или просто исправить,strlen должно помочь). Я на самом деле не согласен сmalloc проверьте предложенное выше, и это заставило меня прокомментировать вообще.
 Thane Brimhall14 февр. 2013 г., 22:08
Отлично спасибо! Увидетьэтот вопрос для одного способа сделать это.
 user39576014 февр. 2013 г., 22:12
Что еще более важно, это должно проверить длину.malloc редко дает сбой, и если это произойдет, вы получите сбой. С другой стороны, этоочень легко вызвать переполнение буфера с этим кодом (или OP 'код, чтобы быть справедливым). Это н'т девяностых.

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

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

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

Ваш ответ на вопрос