为什么我的C模块泄漏内存?

我正在从大文件中读取列表,我最终想将其存储为
array.array
s。因为
map(int, line.split())
非常慢,我写了一个小的C模块,它做了strtok和更快版本的atoi:
inline long
minhashTables_myatoi(const char* s)
{
    int r;
    for (r = 0; *s; r = r * 10 + *s++ - '0');
    return r;
}

static PyObject*
minhashTables_ints(PyObject *self, PyObject *args)
{
    char* s;
    Py_ssize_t slen;

    if(!PyArg_ParseTuple(args, "s#", &s, &slen))
        return NULL;

    long* buf = malloc(sizeof(long) * (slen+1)/2);

    const char* tok = strtok(s, " ");
    buf[0] = minhashTables_myatoi(tok);
    Py_ssize_t i;
    for(i = 1; (tok = strtok(NULL, " ")) != NULL; i++)
        buf[i] = minhashTables_myatoi(tok);

    Py_ssize_t buflen = i;
    PyObject* list = PyList_New(buflen);
    PyObject *o;
        for(i = 0; i < buflen; i++)
    {
        o = PyInt_FromLong(buf[i]);
        PyList_SET_ITEM(list, i, o);
    }
    free(buf);

    return list;
}
所以我的python脚本用字符串调用
ints()
并将其传递给
array.array
构造函数并将结果数组保存在
list
中。 我的问题是,现在脚本泄漏了内存,当然,它没有使用地图而不是
ints()
功能。 使用我自己版本的Pythons
int()
使用C模块也不会泄漏内存。 谢谢你的帮助! 编辑: 要使用此脚本对模块进行valgrind:
import minhashTables

data = ' '.join(map(str, range(10)))
print 'start'
foo = minhashTables.ints(data)
del data
del foo
print 'stop'
而且我跑了
valgrind --tool=memcheck --leak-check=full --show-reachable=yes python test.py
,但是在
start
stop
之间没有来自valgrind的输出,之前和之后都有吨。 编辑:确认泄漏的代码:     import minhashTables
for i in xrange(1000000000):
    data = ' '.join(map(str, range(10, 10000)))
    foo = minhashTables.ints(data)
我必须重新创建字符串,因为strtok会更改它。顺便说一下,将字符串复制到另一个内存位置不会改变行为。     
已邀请:
我建议你看看Valgrind - 它是一个非常有用的工具,可以在C中找到内存泄漏的底部。     
对于所有那些
long
s,你真的需要
malloc
空间吗? 我不熟悉Python / C API,所以这可能是一个糟糕的建议,但是你不能只是迭代字符串并将你找到的每一长时间追加到列表中吗? 即拿这个代码:
static const char* const testString = "12 345  67  8 910 11 1213 141516, 1718";

int main()
{
    const char* i = testString;
    long parseLong = 0;
    int gotLong = 0;

    for (;*i;++i)
    {
        if ('0' <= *i && *i <= '9')
        {
            parseLong = (parseLong * 10) + (*i - '0');
            gotLong = 1;
        }
        else if (gotLong)
        {
            printf("Got: %dn", parseLong);
            parseLong = 0;
            gotLong = 0;
        }
    }

    if (gotLong)
        printf("Got: %dn", parseLong);
}
然后将
printf
替换为
PyList_Append()
等一些合适的pythony-goodness。 除了避免
malloc
,使用更少的内存并且能够安全地直接在常量Python字符串上操作,此代码还处理诸如空字符串,多个空格和数字之间的其他分隔符等极端情况。 编辑:计算多头 如果你想先计算long的数量,那么你可以分配正确长度的Python List,那么你可以添加如下内容:
    for (i = testString;*i;++i)
    {
        const int isdigitoflong = isdigit(*i);

        if (!gotLong && isdigitoflong)
            longCount++;

        gotLong = isdigitoflong;
    }
哪个应该比较快。 编辑2:更好的解析器 这里是一个稍微好一点的解析器版本,它更紧凑,不需要
gotLong
而且不必重复代码来处理最终的长:
    for (i = testString;*i;++i)
    {
        if (isdigit(*i))
        {
            do {
                parseLong = (parseLong * 10) + (*i - '0');
            } while (*++i && isdigit(*i));

            printf("Got: %dn", parseLong);
            parseLong = 0;
        }
    }   
    
试试这个
inline long
    minhashTables_myatoi(const char* s)
    {
        long result=0;
        while((*s)!=''){
            result = result * 10 + (*s- '0');
            s++;
        }
        return result;
    }
    

要回复问题请先登录注册