python - 使用此 Python C 扩展在特定情况下获取总线错误

标签 python c

我正在学习 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);
}

最佳答案

我发现您的代码存在三个问题。

首先,PyList_GetItem 返回借用的引用,PyList_SetItem 窃取引用,这意味着您最终将得到两个指向同一个对象的列表,但对象的引用计数将是 1 而不是 2。这肯定会导致严重的问题(Python 会在某个时候尝试删除已经删除的对象)。

其次,您没有检查错误。您应该检查所有 Python 调用的返回值,如果您检测到问题,请减少您持有的所有引用并返回 NULL

例如:

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

那么,因为第一个问题,你在设置item的时候不得不增加引用:

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;

泄漏引用意味着这个列表永远不会从内存中释放。

关于python - 使用此 Python C 扩展在特定情况下获取总线错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8965442/

相关文章:

python - 将函数作为变量传递,固定一个输入

c - 如何更改Linux文件属性

java - OpenCV 如何检测网络摄像头并比较本地文件以匹配人脸

c - 为什么我不能在嵌套结构中初始化数组?

c - 客户端断开连接后是否可以从客户端套接字读取数据?

python - 我可以使用 Tornado+Celery+RabbitMQ+Redis 吗?

python - 在 Pycharm 中使用 virtualenv 作为 Django IDE

python - 将参数传递给 requests.Session.get 引发 TypeError

python - 将列表中的整数乘以列表中的单词

c++ - 如何在 Linux 上的 c 中休眠或暂停 PThread