2012-01-22 17 views
1

我正在學習C,並且同時嘗試實現Python C擴展,這完美工作直到我將它傳遞給一個相當大的列表爲止......使用此Python C擴展在特定情況下獲取總線錯誤

例..

>>> import shuffle 
>>> shuffle.riffle(range(100)) 

的偉大工程!

>>> shuffle.riffle(range(1000)) 
Bus Error: 10 

有關我的問題是什麼的任何想法?

#include <Python.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

static PyObject *shuffle_riffle(PyObject *self, PyObject *args) 
{ 
    int const MAX_STREAK = 10; 
    int m, f, l, end_range, streak, *current_ptr; 
    double length; 

    PyObject * origList; 
    PyObject * shuffledList; 
    srand((int)time(NULL)); 

    // parse args to list 
    if (! PyArg_ParseTuple(args, "O!", &PyList_Type, &origList)) 
    { 
     return NULL; 
    } 

    length = (int)PyList_Size(origList); 
    current_ptr = (rand() % 2) ? &f : &l; 
    end_range = (int)(length/2) + (rand() % (length > 10 ? (int)(.1 * length) : 2)); 
    shuffledList = PyList_New((int)length); 

    for(m = 0, f = 0, l = (end_range + 1), streak = 0; m < length && l < length && f < end_range + 1; m++, *current_ptr += 1) 
    { 
     double remaining = 1 - m/length; 
     double test = rand()/(double)RAND_MAX; 

     if (test < remaining || streak > MAX_STREAK) 
     { 
      current_ptr = (current_ptr == &f ? &l : &f); 
      streak = 0; 
     } 

     PyList_SetItem(shuffledList, m, PyList_GetItem(origList, *current_ptr)); 
     streak += 1; 
    } 

    // change the pointer to the one that didn't cause the for to exit 
    current_ptr = (current_ptr == &f ? &l : &f); 

    while(m < length) 
    { 
     PyList_SetItem(shuffledList, m, PyList_GetItem(origList, *current_ptr)); 
     m++; 
     *current_ptr += 1; 
    } 



    return Py_BuildValue("O", shuffledList); 

} 

static PyMethodDef ShuffleMethods[] = { 
    {"riffle", shuffle_riffle, METH_VARARGS, "Simulate a Riffle Shuffle on a List."}, 
    {NULL, NULL, 0, NULL} 
}; 

void initshuffle(void){ 
    (void) Py_InitModule("shuffle", ShuffleMethods); 
} 
+0

在哪一行發生總線錯誤? – ouah

+0

@ouah在執行了一些'printf'動作之後,它看起來像是一直到while循環,並在'while'聲明和/或第一次迭代時失敗。 – jondavidjohn

回答

4

我看到你的代碼有三個問題。

首先,PyList_GetItem返回借用的引用和PyList_SetItem搶斷,這意味着你將最終獲得兩個列表指向同一個對象,但對象的引用計數爲1,而不是2。這肯定會導致嚴重的問題倒參考道路(Python會在某個時候嘗試刪除一個已經被刪除的對象)。

其次,您沒有檢查錯誤。您應該檢查所有Python調用的返回值,並且如果您檢測到問題,請將您保留的所有引用遞減並返回NULL

例如:

PyObject *temp = PyList_GetItem(origList, *current_ptr); 
if (temp == NULL) { 
    Py_DECREF(shuffledList); 
    return NULL; 
} 

再者,由於第一個問題,你必須incref設置項目時參考:

PyList_SET_ITEM(shuffledList, m, temp); 
Py_INCREF(temp); 

,因爲你知道你可以使用PyList_SET_ITEM宏這裏shuffledList尚未初始化。

第三,你在泄漏此行的shuffledList對象的引用:

return Py_BuildValue("O", shuffledList); 

這相當於:

Py_INCREF(shuffledList); 
return shuffledList; 

既然你已經擁有的引用(因爲你創造了這個對象),你想直接返回:

return shuffledList; 

泄漏引用ce意味着這個列表永遠不會從內存中釋放。

+0

真棒,感謝您的信息。 – jondavidjohn

+0

張貼編輯...再次,非常感謝,學到了很多東西,讓它工作。 – jondavidjohn

相關問題