cython class 包含 c 字符串;缓冲区溢出?
cython class containing c strings; buffer overrun?
为了学习一点 Cython,我一直在尝试编写一个只包含几个 cstring 的玩具库(对应于 factoral/categorical 数据类型的可用选择)。 class 中指向的字符串正在被覆盖,我的 C/Cython-foo 太小无法弄清楚原因。
结果是这样的:
>>> import coupla
>>> ff = coupla.CouplaStrings(["one", "two"])
>>> ff
write, two
>>> ff
, two
>>> ff
two, two
非常感谢您的帮助!我觉得我要疯了。仅使用 to_cstring_array
和 to_str_list
函数似乎工作正常,但在 class 中它会卡普特。
cdef extern from "Python.h":
char* PyUnicode_AsUTF8(object unicode)
from libc.stdlib cimport malloc, free
cdef char **to_cstring_array(list_str):
"""Stolen from Whosebug:
"""
cdef Py_ssize_t num_strs = len(list_str)
cdef char **ret = <char **>malloc(num_strs * sizeof(char *))
for i in range(num_strs):
ret[i] = PyUnicode_AsUTF8(list_str[i])
return ret
cdef to_str_list(char **cstr_array, Py_ssize_t size):
cdef int i
result = []
for i in range(size):
result.append(bytes(cstr_array[i]).decode("utf-8"))
return result
cdef class CouplaStrings:
cdef char **_strings
cdef Py_ssize_t _num_strings
def __init__(self, strings):
cdef Py_ssize_t num_strings = len(strings)
cdef char **tstrings = <char **> to_cstring_array(strings)
self._num_strings = num_strings
self._strings = tstrings
def __repr__(self):
"""Just for testing."""
return ", ".join(to_str_list(self._strings, self._num_strings))
def __dealloc__(self):
free(self._strings)
编辑:
请参阅 user2357112 的以下回答。 CouplaStrings
的编辑版本似乎避免了这个特殊问题,但我不会保证它的整体正确性。
编辑 2:这是错误的忽略仅用于历史目的
cdef class CouplaStrings:
cdef char **_strings
cdef Py_ssize_t _num_strings
def __init__(self, strings):
cdef Py_ssize_t num_strings = len(strings)
cdef char **ret = <char **> PyMem_Malloc(num_strings * sizeof(char *))
for i in range(num_strings):
ret[i] = <char *> PyMem_Realloc(PyUnicode_AsUTF8(strings[i]),
sizeof(char *))
self._num_strings = num_strings
self._strings = ret
def __repr__(self):
"""Just for testing."""
return ", ".join(to_str_list(self._strings, self._num_strings))
def __dealloc__(self):
PyMem_Free(self._strings)
您未能说明所有权和内存管理。
PyUnicode_AsUTF8
返回的 UTF-8 编码由调用的字符串对象 PyUnicode_AsUTF8
拥有,当该字符串消失时它会被回收。为了防止字符串对象在您的对象死亡之前死亡,您的对象需要保留对字符串对象的 (Python) 引用。或者,您可以将 UTF-8 编码复制到您自己分配的内存中,并负责自行释放该内存。
否则,您将只有一个悬挂指针数组。
为了学习一点 Cython,我一直在尝试编写一个只包含几个 cstring 的玩具库(对应于 factoral/categorical 数据类型的可用选择)。 class 中指向的字符串正在被覆盖,我的 C/Cython-foo 太小无法弄清楚原因。
结果是这样的:
>>> import coupla
>>> ff = coupla.CouplaStrings(["one", "two"])
>>> ff
write, two
>>> ff
, two
>>> ff
two, two
非常感谢您的帮助!我觉得我要疯了。仅使用 to_cstring_array
和 to_str_list
函数似乎工作正常,但在 class 中它会卡普特。
cdef extern from "Python.h":
char* PyUnicode_AsUTF8(object unicode)
from libc.stdlib cimport malloc, free
cdef char **to_cstring_array(list_str):
"""Stolen from Whosebug:
"""
cdef Py_ssize_t num_strs = len(list_str)
cdef char **ret = <char **>malloc(num_strs * sizeof(char *))
for i in range(num_strs):
ret[i] = PyUnicode_AsUTF8(list_str[i])
return ret
cdef to_str_list(char **cstr_array, Py_ssize_t size):
cdef int i
result = []
for i in range(size):
result.append(bytes(cstr_array[i]).decode("utf-8"))
return result
cdef class CouplaStrings:
cdef char **_strings
cdef Py_ssize_t _num_strings
def __init__(self, strings):
cdef Py_ssize_t num_strings = len(strings)
cdef char **tstrings = <char **> to_cstring_array(strings)
self._num_strings = num_strings
self._strings = tstrings
def __repr__(self):
"""Just for testing."""
return ", ".join(to_str_list(self._strings, self._num_strings))
def __dealloc__(self):
free(self._strings)
编辑:
请参阅 user2357112 的以下回答。 CouplaStrings
的编辑版本似乎避免了这个特殊问题,但我不会保证它的整体正确性。
编辑 2:这是错误的忽略仅用于历史目的
cdef class CouplaStrings:
cdef char **_strings
cdef Py_ssize_t _num_strings
def __init__(self, strings):
cdef Py_ssize_t num_strings = len(strings)
cdef char **ret = <char **> PyMem_Malloc(num_strings * sizeof(char *))
for i in range(num_strings):
ret[i] = <char *> PyMem_Realloc(PyUnicode_AsUTF8(strings[i]),
sizeof(char *))
self._num_strings = num_strings
self._strings = ret
def __repr__(self):
"""Just for testing."""
return ", ".join(to_str_list(self._strings, self._num_strings))
def __dealloc__(self):
PyMem_Free(self._strings)
您未能说明所有权和内存管理。
PyUnicode_AsUTF8
返回的 UTF-8 编码由调用的字符串对象 PyUnicode_AsUTF8
拥有,当该字符串消失时它会被回收。为了防止字符串对象在您的对象死亡之前死亡,您的对象需要保留对字符串对象的 (Python) 引用。或者,您可以将 UTF-8 编码复制到您自己分配的内存中,并负责自行释放该内存。
否则,您将只有一个悬挂指针数组。