【问题标题】:cython class containing c strings; buffer overrun?包含 c 字符串的 cython 类;缓冲区溢出?
【发布时间】:2019-03-24 22:16:14
【问题描述】:

尝试学习一点 Cython,我一直在尝试编写一个仅包含几个 cstrings 的玩具库(对应于因子/分类数据类型的可用选择)。类中指向的字符串被覆盖,而我的 C/Cython-foo 太小,无法弄清楚原因。

结果是这样的:

>>> import coupla
>>> ff = coupla.CouplaStrings(["one", "two"])
>>> ff
write, two
>>> ff
, two
>>> ff
two, two

非常感谢您的帮助!我觉得我快疯了。仅使用 to_cstring_arrayto_str_list 函数似乎可以正常工作,但在类中它会失败。

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 Stackoverflow:
    https://stackoverflow.com/questions/17511309/fast-string-array-cython/17511714#17511714
    """
    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)

【问题讨论】:

  • 编辑后的版本更差!你已经重新分配了 Python 认为它正在管理的内存。你想计算出大小,malloc一个新的但内存,然后复制。或者只是避免使用 C 字符串,因为它们总是很痛苦。
  • 哦不!好吧,我想这并不奇怪。回到这里的绘图板。

标签: python cython c-strings


【解决方案1】:

您没有考虑所有权和内存管理。

PyUnicode_AsUTF8 返回的 UTF-8 编码由被调用的字符串对象 PyUnicode_AsUTF8 拥有,并在该字符串死亡时被回收。为了防止字符串对象在您的对象之前死亡,您的对象需要保留对字符串对象的(Python)引用。或者,您可以将 UTF-8 编码复制到您自己分配的内存中,并自行负责释放该内存。

否则,您将只有一个悬空指针数组。

【讨论】:

  • 谢谢,这给了我一个新的视角。我已经编写了修复原始问题的代码,但我不确定它是否正确。我会用它来编辑答案。
猜你喜欢
  • 2021-12-10
  • 2022-11-30
  • 2013-10-22
  • 2011-12-22
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 2014-11-07
  • 2023-03-25
相关资源
最近更新 更多