python - 为什么这个 C 方法会出现段错误?

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

我正在用 C 语言编写一个不可变链表类,但其中一种方法是神秘的段错误。该代码旨在大致等同于此:

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)

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

最佳答案

我正在阅读文档:


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

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


您在末尾缺少 NULL 值。

编辑:Ho 并且您还想检查函数是否在内存故障的情况下返回 NULL

关于python - 为什么这个 C 方法会出现段错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4057611/

相关文章:

python 3 typing varadic "apply"样式定义

python - 如何更改 PyGame 中声音或音乐的音量?

python - 从数组及其转置创建邻接矩阵

c - 将字符串初始化为 {0, } 有什么作用?

c++ - 如何在 C++ 中跟踪无效指针?

c++ - OpenCV 简单代码编译成功但在运行时崩溃

python - 如何将字符串转换为函数的参数?

c - While 和 While 循环是否意外退出?

java - 我需要知道这两句话在编程中是否相同

c - 这怎么是段错误 11?只有 5x200 个条目数组