Search code examples
cpython

PyList_New alloc items number greater than real used, how to initialize those unused items?


I have a C program to use python embedded.

In my C code, I use PyList_New(10) to alloc 10 items, and I use PyList_SetItem() to initialize them, but actually I maybe will not use all of them (e.g. I used 8). So the fact is 8 of the list items has been initialized, and 2 of them not (they will be NULL in C code). After initialization, I called a python function with PyObject_Call(). Finally, my program was broken in python script, because the NULL pointer.

So, I want to know how to initialize the 2 unused items (maybe convert it to None)

PS: I don't want to use PyList_Append, because I heard it's a little inefficient

My C code is like this:

PyObject *py_func;

int my_func(PyObject *item_value)
{
    my_list = PyList_New(10);

    for (i = 0; i < 8; i++) {
        PyList_SetItem(my_list, i, item_value);
    }

    /* ... */
    PyObject_Call(py_func, my_list, NULL);

    /* ... */
    return 0;
}

My python code is like this:

def my_func(aList):
    for item in aList:
        if item:
            # do something

========================================

here is python interpreter broken stack:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  listiter_next (it=0x7f354defe5c0) at Objects/listobject.c:2782
2782    Objects/listobject.c: No such file or directory.
[Current thread is 1 (Thread 0x7f3558c2e840 (LWP 607))]
(gdb) bt
#0  listiter_next (it=0x7f354defe5c0) at Objects/listobject.c:2782
#1  0x00007f355840cbaa in _PyEval_EvalFrameDefault (f=<optimized out>, 
    throwflag=<optimized out>) at Python/ceval.c:3071
#2  0x00007f3558407b6e in _PyEval_EvalCodeWithName (_co=0x7f3558aba780, 
    globals=<optimized out>, locals=locals@entry=0x0, 
    args=args@entry=0x7f35501b2fe0, argcount=2, kwnames=kwnames@entry=0x0, 
    kwargs=kwargs@entry=0x8, kwcount=kwcount@entry=0, kwstep=kwstep@entry=2, 
    defs=defs@entry=0x0, defcount=defcount@entry=0, kwdefs=kwdefs@entry=0x0, 
    closure=closure@entry=0x0, name=name@entry=0x0, 
    qualname=qualname@entry=0x0) at Python/ceval.c:4139
#3  0x00007f3558408193 in PyEval_EvalCodeEx (_co=<optimized out>, 
    globals=<optimized out>, locals=locals@entry=0x0, 
    args=args@entry=0x7f35501b2fe0, argcount=<optimized out>, 
    kws=kws@entry=0x0, kwcount=kwcount@entry=0, defs=defs@entry=0x0, 
    defcount=defcount@entry=0, kwdefs=0x0, closure=0x0) at Python/ceval.c:4160
#4  0x00007f3558349076 in function_call (func=0x7f3550a20268, 
    arg=0x7f35501b2fc8, kw=0x0) at Objects/funcobject.c:604
#5  0x00007f3558316c2a in PyObject_Call (func=0x7f3550a20268, 
    args=<optimized out>, kwargs=<optimized out>) at Objects/abstract.c:2246

Solution

  • I don't understand how your Python and C code go together with my_func defined in both, but if your problem indeed comes from uninitialized values in the list, it is because you didn't initialize them to something that is valid in Python. The C NULL pointer istn't. Py_None is. From the docs:

    Py_None is the C name for the special Python object None. It is a genuine Python object rather than a NULL pointer, which means “error” in most contexts, as we have seen.

    So just set the remaining elements of your list to Py_None (using Py_INCREF) and you're set.

    for (i = 0; i < 8; i++) {
        PyList_SetItem(my_list, i, item_value);
    }
    for (; i < PyList_Size(my_list); i++) {
        Py_INCREF(Py_None);
        PyList_SetItem(my_list, i, Py_None);
    }