Pointers and “Storing unsafe C derivative of temporary Python reference”

南楼画角 提交于 2019-12-10 03:21:15

问题


I was writing code to store a (potentially) very large integer value into an array of chars referenced by a pointer. My code looks like this:

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

When I tried compiling the code, I got the error, "Storing unsafe C derivative of temporary Python reference" at the commented line. My question is this: which temporary Python reference am I deriving in C and storing, and how do I fix it?


回答1:


The problem is that under the hood a temporary variable is being created to hold the array before the assignment to self.Array and it will not be valid once the method exits.

Note that the documentation advises:

the C-API functions for allocating memory on the Python heap are generally preferred over the low-level C functions above as the memory they provide is actually accounted for in Python’s internal memory management system. They also have special optimisations for smaller memory blocks, which speeds up their allocation by avoiding costly operating system calls.

Accordingly you can write as below, which seems to handle this use case as intended:

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's answer does a pretty good job of cleaning up the code and "doing everything properly" (most importantly the deallocation of memory which was missing from __dealloc__ in the question!).

The actual issue causing the error is that you haven't cimported malloc. Because of this Cython assumes that malloc is a Python function, returning a Python object which you want to cast to a char*. At the top of your file, add

from libc.stdlib cimport malloc, free

and it'll work. Or alternatively use PyMem_Malloc (cimporting it) as @rll does and that will work fine too.



来源:https://stackoverflow.com/questions/32371919/pointers-and-storing-unsafe-c-derivative-of-temporary-python-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!