Python C扩展段错误

时间:2018-04-11 02:13:27

标签: python python-c-api

我第一次冒险进入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

它应该迭代列表列表,直到它找到一个大于给定时间的值。然后返回该值。正如我所提到的,它正确地返回了答案,但是如果我足够多次调用它,那就是段错误。

我的直觉是,这与引用计数有关,但我不熟悉这个概念,知道这是否是直接原因。

任何帮助都将不胜感激。

1 个答案:

答案 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函数的返回值是否失败,并正确处理它。