Python ctypes:如何释放内存?获取无效指针错误

Posted

技术标签:

【中文标题】Python ctypes:如何释放内存?获取无效指针错误【英文标题】:Python ctypes: how to free memory? Getting invalid pointer error 【发布时间】:2012-11-06 21:20:05 【问题描述】:

我想从带有 ctypes 的 C/C++ 库中获取一些字符串到 python 中。我的代码如下所示:

lib 中的代码:

const char* get(struct something *x) 

    [...]
    // buf is a stringstream
   return strdup(buf.str().c_str());


void freeme(char *ptr)

    free(ptr);

Python 代码:

fillprototype(lib.get, c_char_p, POINTER(some_model)])
fillprototype(lib.freeme, None, [c_char_p])

// what i want to do here: get a string into python so that i can work
// with it and release the memory in the lib.
c_str = lib.get(some_model)
y = ''.join(c_str)
lib.freeme(c_str) 

如果我 print() c_str,一切都在那里。问题出在(或似乎是)在 Python 的最后一行。我无法释放内存 - 库得到了错误的指针。我在这里做错了什么? (请不要建议 boost::python 左右)。

*** glibc detected *** python2: munmap_chunk(): invalid pointer: 0x00000000026443fc ***

【问题讨论】:

为什么get 返回const char *freeme 却期望char *?你希望你的调用者抛弃 const 吗? 好点。不幸的是,这并没有解决问题。 这是 32 位还是 64 位代码?你知道错误发生在哪一行代码吗?不一定是free(ptr) 64 位。我不知道它是哪一行:gdb bt 只显示了很多 python 的东西。但我只是在 get() 和 freeme() 中打印了 ptr 并得到了:“allocated memory ptr: 0x2592a20”、“free memory ptr: 0x7f2bf5fad3fc”、“python2: munmap_chunk(): invalid pointer: 0x00007f2bf5fad3fc” 我敢打赌 Python 会创建自己的字符串,就像 C++ 会创建 string c_str = lib.get(some_model); 【参考方案1】:

正如 David Schwartz 指出的,如果将 restype 设置为 c_char_p,ctypes 将返回一个常规 Python 字符串对象。解决此问题的一种简单方法是使用 void * 并转换结果:

字符串.c:

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

char *get(void)

    char *buf = "Hello World";
    char *new_buf = strdup(buf);
    printf("allocated address: %p\n", new_buf);
    return new_buf;


void freeme(char *ptr)

    printf("freeing address: %p\n", ptr);
    free(ptr);

Python 用法:

from ctypes import *

lib = cdll.LoadLibrary('./string.so')
lib.freeme.argtypes = c_void_p,
lib.freeme.restype = None
lib.get.argtypes = []
lib.get.restype = c_void_p

>>> ptr = lib.get()
allocated address: 0x9facad8
>>> hex(ptr)
'0x9facad8'
>>> cast(ptr, c_char_p).value
'Hello World'
>>> lib.freeme(ptr)
freeing address: 0x9facad8

您还可以使用c_char_p 的子类。事实证明,ctypes 不会为简单类型的子类调用getfunc

class c_char_p_sub(c_char_p):
    pass

lib.get.restype = c_char_p_sub

value 属性返回字符串。您可以将freeme 的参数保留为更通用的c_void_p。它接受任何指针类型或整数地址。

【讨论】:

释放时我不得不强制转换指针,例如:lib.freeme(cast(ptr, c_char_p)),否则我在 python3 中得到这个错误:ctypes.ArgumentError: argument 1: &lt;class 'TypeError'&gt;: wrong type【参考方案2】:

释放内存的一个便宜的技巧是在 C 中静态分配它并且从不释放它。当然,只有在您知道所需的最大量并且在第二次调用您的例程之前知道您已完成它的情况下才有效。

static char _externalStringBuffer[MAX_BUFFER_SIZE];
char *get() 
    // put characters in _externalStringBuffer
    return _externalStringBuffer;

【讨论】:

这是一个非常糟糕的建议,因为多次调用该函数会改变现在的全局变量。 真的! ..这就是为什么我包括“只有在……你知道在第二次调用你的例程之前你已经完成了[它]”时才有效。

以上是关于Python ctypes:如何释放内存?获取无效指针错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 numpy/ctypes 公开 C 分配的内存缓冲区的更安全方法?

ctypes给扩展模块中的函数传递数组和结构体

如何使用 ctypes 在 python 中正确包装 C API?

如何在ctypes中传回指针?

Python/Windows/ctypes:调用 WaitForMultipleObjects 后如何获取进程返回状态?

使用 ctypes 和 wchar_t 时如何从 C++ 获取字符串到 python?