2015-09-03 4 views
6

Я писал код для хранения (потенциально) очень большого целочисленного значения в массиве chars, на который ссылается указатель. Мой код выглядит следующим образом:Указатели и «Сохранение небезопасной производной C от временной ссылки на Python»

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 

Когда я пытался компилировать код, я получил ошибку «Сохранение небезопасных производной C референции временного Python» в комментировал линии. Мой вопрос таков: какую временную ссылку на Python я получаю на C и храня, и как ее исправить?

ответ

3

Проблема заключается в том, что под капотом создается временная переменная для хранения массива до назначения self.Array, и это будет недействительным после выхода метода.

Обратите внимание, что documentation советует:

функции C-API для выделения памяти в куче Python, как правило, предпочтительнее функции низкого уровня C выше в качестве памяти они обеспечивают фактически учитываются в Питоне внутренней системы управления памятью. Они также имеют специальную оптимизацию для небольших блоков памяти, что ускоряет их распределение, избегая дорогостоящих вызовов операционной системы.

Соответственно, вы можете написать, как показано ниже, который, кажется, справиться с этим случаем использования по назначению:

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

@ ответ RLL в делает очень хорошую работу по очистке коды и «делает все правильно» (самое главное освобождение памяти, которое отсутствовало в __dealloc__ в вопросе!).

Фактическая проблема, вызвавшая ошибку, заключается в том, что у вас нет cimport ed malloc. Из-за этого Cython предполагает, что malloc является функцией Python, возвращая объект Python, который вы хотите отнести к char*. В верхней части файла добавить

from libc.stdlib cimport malloc, free 

и он будет работать. Или, альтернативно, используйте PyMem_Malloc (cimporting it), как это делает @rll, и это тоже сработает.

 Смежные вопросы

  • Нет связанных вопросов^_^