cython - 存储临时 Python 引用的不安全 C 派生
问题描述
考虑以下人为的 Cython 函数来加入字符串列表:
# cython: language_level=3
cpdef test_join():
""" ["abc", "def", "ghi"] -> "abcdefghi" """
cdef:
list lines = ["abc", "def", "ghi"]
char* out = ""
char* line = ""
int i
for i in range(len(lines)):
line = lines[i]
out = out + line
return out
它将无法编译,并出现以下错误:
存储临时 Python 引用的不安全 C 派生
我假设这与line
类型char*
和不断重新分配有关。我已经看到了一个类似问题的答案,但无法为这个基本示例修改该答案。(而且它还涉及大量我不熟悉的 C-API。)
如何修改上述函数以按预期编译和返回?
更广泛地说,我想更好地理解这个错误。Commit 37e4a20有一点解释:
从
char*
一个临时的 Python 字符串对象中获取一个...只有当这样的指针被分配给一个变量并且因此会超过字符串本身的生命周期时,才会引发编译时错误。
更新:为了进一步简化事情,看起来这是导致问题的分配:
cpdef int will_succeed():
cdef char* a = b"hello"
cdef char* b = b" world"
print(a + b) # no new assignment
return 1
cpdef will_fail():
cdef char* a = b"hello"
cdef char* b = b" world"
a = a + b # won't compile
return a
我怀疑可能有一种更合适的方法可以使用来自string.pxd
/的东西来做到这一点string.h
,但我在 C 内存管理和效率方面相当薄弱:
from libc.string cimport strcat, strcpy
cpdef use_strcat():
cdef char out[1024]
strcpy(out, b"")
cdef char* a = b"hello"
cdef char* b = b" world"
strcat(out, a)
strcat(out, b)
return out
解决方案
我认为问题在于
out = out + line
Cython 没有+
为 C 字符串定义运算符。相反,它将它们转换为 Python 字符串并将它们连接起来:
tmp1 = str(out)
tmp2 = str(line)
tmp3 = tmp1 + tmp2
out = get_c_string_from(tmp3)
out
因此,一旦tmp3
被销毁(即立即),它就会成为无效指针。
我会避免使用strcat
,因为它对于重复使用不是很有效。而是跟踪当前的字符串长度并自己复制数据。鉴于您的长度未知,您可能希望分配字符串malloc
(在这种情况下,您负责释放它)
from libc.stdlib cimport free, malloc, realloc
from libc.string cimport memcpy
from cython import Py_ssize_t
cdef char *line
cdef Py_ssize_t i
cdef Py_ssize_t length = 0
cdef Py_ssize_t incrlength
cdef char *out = <char *>malloc(1) # Reallocate as needed
try:
out[0] = b'\x00' # keep C-strings null-terminated
for i in range(len(lines)):
line = lines[i]
incrlength = len(line)
out = <char *>realloc(out, length + incrlength + 1)
memcpy(out + length, line, incrlength)
length += incrlength
out[length] = '\x00' # keep C-strings null-terminated
return out # autoconversion back to a Python string
finally:
free(out)
这是我认为您应该做的粗略概述,并且没有经过真正的测试。
推荐阅读
- javascript - 来自rest API的日期时间的Javascript错误输出
- logstash - Kibana 不断抱怨 dateparse 和 grokparse 错误
- c - 红黑树帮助 C
- c# - 如何使用一个 Viewmodel,多个视图/页面可以绑定到 Viewmodel 中的变量
- typescript - 将自定义持续时间字符串转换为时刻持续时间以进行比较
- node.js - MS Bot Framework V4-Node.js 中的对话中断
- php - 值未正确插入数组
- mongodb - Mongodb graphLookup cond 进入 connectToField
- firebase - 无需用户身份验证的云 Firestore (firebase) 的安全安全规则
- laravel - Laravel 中酒店订房问题查询