2010-10-30 58 views
1

我正在C中編寫一個不可變鏈接列表類,但有一種方法是神祕地斷續。代碼的目的是大致相同的:這種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); 
} 

如果我運行這段代碼,它出現segfaults的最後一行:

from pysistence.persistent_list import PList  

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

我確信我只是在做一些愚蠢的事情,但我不知道它是什麼。有什麼我失蹤?

+0

僅供參考的情況下返回NULL(它是更晚):代碼也有引用計數錯誤。你不應該在'PList_cons()'中調用'Py_INCREF()'。 – 2014-10-14 09:56:39

回答

4

我從文檔閱讀:


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

呼叫一個可調用Python對象調用,用的PyObject的*參數個數可變。參數以可變數量的參數提供,然後爲NULL。返回成功調用的結果,或失敗時返回NULL。


您在最後缺少NULL值。

編輯:浩,你還需要檢查是否函數內存故障

+0

Doh!看,我說這是愚蠢的。 :-) – 2010-10-30 07:07:17