2015-09-03 12 views
6

Pisałem kod, aby zapisać (potencjalnie) bardzo dużą liczbę całkowitą w tablicy chars, do której odwołuje się wskaźnik. Mój kod wygląda następująco:Wskaźniki i "Przechowywanie niebezpiecznej pochodnej C tymczasowego odwołania do Pythona"

cdef class Variable: 

    cdef unsigned int Length 
    cdef char * Array 

    def __cinit__(self, var, length): 
     self.Length = length 
     self.Array = <char *>malloc(self.Length * sizeof(char)) # Error 
     for i in range(self.Length): 
      self.Array[i] = <char>(var >> (8 * i)) 

    def __dealloc__(self): 
     self.Array = NULL 

Kiedy próbowałem kompilacji kodu, mam błędzie „Przechowywanie niebezpieczną pochodną C czasowego odniesienia Pythona” w komentowanej linii. Moje pytanie brzmi: który tymczasowy odnośnik Pythona czerpię z C i przechowywania, i jak mogę to naprawić?

Odpowiedz

3

Problem polega na tym, że pod maską tworzona jest zmienna tymczasowa przechowująca tablicę przed przypisaniem do self.Array i nie będzie ona poprawna po wyjściu z metody.

Należy zauważyć, że documentation radzi:

funkcji C API dla alokacji pamięci na hałdy Python są ogólnie korzystne w porównaniu z funkcjami niskiego poziomu C powyżej, w pamięci zapewniają one faktycznie uwzględnione w Pythona system zarządzania pamięcią wewnętrzną. Mają także specjalne optymalizacje dla mniejszych bloków pamięci, co przyspiesza ich alokację, unikając kosztownych wywołań systemu operacyjnego.

Odpowiednio można napisać jak poniżej, co wydaje się obsługiwać ten przypadek użycia zgodnie z przeznaczeniem:

from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free 

cdef class Variable: 

    cdef unsigned int Length 
    cdef char * Array 

    def __cinit__(self, var,size_t length): 
     self.Length = length 
     self.Array = <char *>PyMem_Malloc(length * sizeof(char)) 
     #as in docs, a good practice 
     if not self.Array: 
      raise MemoryError() 

     for i in range(self.Length): 
      self.Array[i] = <char>(var >> (8 * i)) 

    def __dealloc__(self): 
     PyMem_Free(self.Array) 
2

@ odpowiedź RLL za robi bardzo dobrą robotę czyszczenia kodu i „robi wszystko prawidłowo” (co najważniejsze, dealokacja pamięci, której brakowało w pytaniu __dealloc__!).

Rzeczywistym problemem powodującym błąd jest to, że nie masz cimport wyd malloc. Z tego powodu Cython zakłada, że ​​malloc jest funkcją Pythona, zwracającą obiekt Pythona, który ma być rzutowany na char*. U góry pliku należy dodać

from libc.stdlib cimport malloc, free 

i będzie działać. Lub alternatywnie użyj PyMem_Malloc (cimporting to) jak @ rll i to też działa dobrze.