2012-11-18 9 views
14

Ich möchte eine Zeichenfolge aus einer C/C++ - Bibliothek mit Ctypes in Python erhalten. Mein Code sieht wie folgt aus:Python Ctypes: Wie Speicher frei? Erhalten ungültiger Zeiger Fehler

-Code in lib:

const char* get(struct something *x) 
{ 
    [...] 
    // buf is a stringstream 
    return strdup(buf.str().c_str()); 
} 

void freeme(char *ptr) 
{ 
    free(ptr); 
} 

Python-Code:

fillprototype(lib.get, c_char_p, POINTER(some_model)]) 
fillprototype(lib.freeme, None, [c_char_p]) 

// what i want to do here: get a string into python so that i can work 
// with it and release the memory in the lib. 
c_str = lib.get(some_model) 
y = ''.join(c_str) 
lib.freeme(c_str) 

Wenn i print() c_str, alles da ist. Problem ist (oder scheint) in der letzten Python-Zeile. Ich kann den Speicher nicht freigeben - die Bibliothek bekommt einen falschen Zeiger. Was mache ich hier falsch? (Und bitte nicht boost :: python oder so vorschlagen).

*** glibc detected *** python2: munmap_chunk(): invalid pointer: 0x00000000026443fc *** 
+2

Warum gibt 'get' ein' const char * 'zurück, aber' freeme' erwartet 'char *'? Erwarten Sie, dass Ihre Anrufer const wegwerfen? –

+0

guter punkt .. leider hat das das problem nicht gelöst. –

+0

Ist das 32-Bit- oder 64-Bit-Code? Und wissen Sie, in welcher Codezeile der Fehler aufgetreten ist? Es ist nicht unbedingt das 'free (ptr)'. –

Antwort

18

Als David Schwartz wies darauf hin, wenn Sie restype auf c_char_p gesetzt, kehrt ctypes ein reguläres Objekt Python-String. Eine einfache Möglichkeit, dies zu umgehen ist eine void * zu verwenden und das Ergebnis werfen:

string.c:

#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 

char *get(void) 
{ 
    char *buf = "Hello World"; 
    char *new_buf = strdup(buf); 
    printf("allocated address: %p\n", new_buf); 
    return new_buf; 
} 

void freeme(char *ptr) 
{ 
    printf("freeing address: %p\n", ptr); 
    free(ptr); 
} 

Python Nutzung:

from ctypes import * 

lib = cdll.LoadLibrary('./string.so') 
lib.freeme.argtypes = c_void_p, 
lib.freeme.restype = None 
lib.get.argtypes = [] 
lib.get.restype = c_void_p 

>>> ptr = lib.get() 
allocated address: 0x9facad8 
>>> hex(ptr) 
'0x9facad8' 
>>> cast(ptr, c_char_p).value 
'Hello World' 
>>> lib.freeme(ptr) 
freeing address: 0x9facad8 

Sie können auch eine Verwendung Unterklasse von c_char_p. Es stellt sich heraus, dass ctypes getfunc für eine Unterklasse eines einfachen Typs nicht aufruft.

class c_char_p_sub(c_char_p): 
    pass 

lib.get.restype = c_char_p_sub 

Das value Attribut gibt die Zeichenfolge zurück. Sie können den Parameter für freeme als generischen c_void_p belassen. Das akzeptiert jeden Zeigertyp oder Integer-Adresse.

+1

Das hat perfekt funktioniert, vielen Dank! –

+0

Gleich rechts. Vielen Dank. –