我第一次冒险进入C扩展,对C也有些新意。我有一个工作的C扩展,但是,如果我在python中反复调用该实用程序,我最终会得到一个分段错误:11。
#include <Python.h>
static PyObject *getasof(PyObject *self, PyObject *args) {
PyObject *fmap;
long dt;
if (!PyArg_ParseTuple(args, "Ol", &fmap, &dt))
return NULL;
long length = PyList_Size(fmap);
for (int i = 0; i < length; i++) {
PyObject *event = PyList_GetItem(fmap, i);
long dti = PyInt_AsLong(PyList_GetItem(event, 0));
if (dti > dt) {
PyObject *output = PyList_GetItem(event, 1);
return output;
}
}
Py_RETURN_NONE;
};
函数args是 时间序列(列表清单):ex [[1,'a'],[5,'b']] 一个时间(长):前4
它应该迭代列表列表,直到它找到一个大于给定时间的值。然后返回该值。正如我所提到的,它正确地返回了答案,但是如果我足够多次调用它,那就是段错误。
我的直觉是,这与引用计数有关,但我不熟悉这个概念,知道这是否是直接原因。
任何帮助都将不胜感激。
答案 0 :(得分:3)
“我的直觉是,这与引用计数有关......”你的直觉是正确的。
PyList_GetItem
会返回借用的引用,这意味着您的函数不会“拥有”对该项的引用。所以这里有一个问题:
PyObject *output = PyList_GetItem(event, 1);
return output;
您没有对该项目的引用,但您将其返回给调用者,因此调用者也不拥有引用。如果在调用者仍在尝试使用该项时垃圾收集,则调用者将遇到问题。因此,在返回之前,您需要增加项目的引用计数:
PyObject *output = PyList_GetItem(event, 1);
Py_INCREF(output);
return output;
假设PyList_GetItem(event, 1)
没有失败!除PyArg_ParseTuple
之外,您不检查C API函数的返回值,这意味着您假设输入参数始终具有您期望的确切结构。在测试代码并弄清楚其工作原理时,这很好,但最终你应该检查C API函数的返回值是否失败,并正确处理它。