为什么这个C方法是segfaulting?

时间:2010-10-30 06:49:03

标签: python c segmentation-fault python-c-extension

我在C中编写了一个不可变的链表类,但是一种方法是神秘的segfaulting。该代码旨在大致相当于:

class PList(object):
    def __init__(self, first, rest=None):
        self.first = first
        self.rest = rest

    def cons(self, item):
        return PList(item, self)

这是我的代码:

#include <Python.h>
#include <structmember.h>

static PyTypeObject PListType;

typedef struct PListStruct{
  PyObject_HEAD
  PyObject *first;
  struct PListStruct *rest;
} PList;

static PyMemberDef plist_members[] = {
  {"first", T_OBJECT_EX, offsetof(PList, first), READONLY, "First element"},
  {"rest", T_OBJECT_EX, offsetof(PList, rest), READONLY, "Rest of the list"},
  {NULL}
};

static PyObject *
PList_cons(PList *self, PyObject *arg)
{
  PList *new_list = PyObject_CallFunctionObjArgs(&PListType, arg, self);
  Py_INCREF(new_list);
  return new_list;
}

static PyMethodDef plist_methods[] = {
  {"cons", (PyCFunction)PList_cons, METH_O, "Add an item to the list"},
  {NULL}
};


static void PList_dealloc(PList *self)
{
  Py_XDECREF(self->first);
  Py_XDECREF(self->rest);
  self->ob_type->tp_free((PyObject*)self);
}

static int
PList_init(PList *self, PyObject *args, PyObject *kwds)
{
  PyObject *first=NULL, *rest=NULL, *tmp;

  static char *kwlist[] = {"first", "rest", NULL};
  if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
                                    &first, &rest))
    return -1;

  if (first){
    tmp = self->first;
    Py_INCREF(first);
    self->first = first;
    Py_XDECREF(tmp);
  }

  if (rest) {
    tmp = self->rest;
    Py_INCREF(rest);
    self->rest = rest;
    Py_XDECREF(tmp);
  }
  else {
    tmp = self->rest;
    Py_INCREF(Py_None);
    self->rest = Py_None;
    Py_XDECREF(tmp);
  }

  return 0;
}

static PyTypeObject PListType= {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "pysistence.persistent_list.PList",             /*tp_name*/
    sizeof(PList), /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)PList_dealloc,                         /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "Persistent list",           /* tp_doc */
    0,                       /* tp_traverse */
    0,                       /* tp_clear */
    0,                       /* tp_richcompare */
    0,                       /* tp_weaklistoffset */
    0,                       /* tp_iter */
    0,                       /* tp_iternext */
    plist_methods,          /* tp_methods */
    plist_members,                       /* tp_members */
    0,                       /* tp_getset */
    0,                       /* tp_base */
    0,                       /* tp_dict */
    0,                       /* tp_descr_get */
    0,                       /* tp_descr_set */
    0,                       /* tp_dictoffset */
    (initproc)PList_init,   /* tp_init */
    0,                       /* tp_alloc */
    0,                       /* tp_new */
};

#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif

PyMODINIT_FUNC
initpersistent_list(void)
{
  PyObject *m;

  PListType.tp_new = PyType_GenericNew;
  if (PyType_Ready(&PListType) < 0)
    return;

  m = Py_InitModule3("pysistence.persistent_list", 0,
                     "Docstring");
  Py_INCREF(&PListType);
  PyModule_AddObject(m, "PList", (PyObject*)&PListType);
}

如果我运行此代码,则会在最后一行显示段错误:

from pysistence.persistent_list import PList    

p = PList(1)
p = PList(2, p)
p = p.cons(3)

我确定我只是在做一些愚蠢的事,但我不知道它是什么。有什么我想念的吗?

1 个答案:

答案 0 :(得分:4)

我正在阅读文档:


PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ..., NULL)
    Return value: New reference.

使用可变数量的PyObject *参数调用可调用的Python对象。参数以可变数量的参数提供,后跟NULL。返回成功时调用的结果,或失败时返回NULL。


你最后错过了NULL值。

编辑:Ho,您还想检查在内存发生故障时函数是否返回NULL