Python ctypes.string_at 释放行为
我正在使用ctypes这个工具,把一些C语言写的函数从一个DLL文件中暴露给Python脚本。这里有一个函数返回的是一个动态大小的字符数组,我想在Python中读取这个数组的内容,同时也希望在用完这个数组后能正确释放它占用的内存。
下面是一个C语言的示例代码:
...
#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) char * WINAPI get_str()
{
int str_len = ... // figure out how long it is gonna be and set it here
char *ary = (char *)malloc(sizeof(char) * str_len);
// populate the array
...
ary[str_len - 1] = '\0';
return ary;
}
#ifdef __cplusplus
}
#endif
我编译了我的DLL,把它复制到一个可以找到的地方,然后用下面的Python代码:
import ctypes
my_dll = ctypes.WinDLL("MyDLLName.dll")
some_str = ctypes.string_at(my_dll.get_str())
print some_str
这段代码的运行效果和预期的一样都没问题。我的问题是:因为ctypes.string_at会在指定的内存位置创建一个字符串,当some_str在Python解释器中超出作用域时,那块内存会被垃圾回收吗?还是说我需要手动释放它呢?
1 个回答
6
string_at
是用来创建一个新的 Python 字符串,这个字符串会在新的内存位置上生成,和你最开始调用它的内存位置完全没有关系。
Python 或者 ctypes 并不知道你原生代码返回了什么 - 对它来说,这只是一个数字(在这个情况下,它恰好是一个有效的指针)。
所以,简单来说,如果你写了 C 代码来分配内存,那么你也应该写相应的 C 代码来释放这块内存 - 然后从使用 ctypes 的 Python 代码中调用这个释放内存的 C 代码。
对于像这样的简单脚本和示例,因为你知道这是一个简单的分配字符串,你可以直接在 Python 端使用 ctypes 调用系统的 free
函数来释放它。
也就是说,把返回的指针存储在一个 Python 变量中:(你可以选择将它转换为合适的 ctypes 指针类型),在运行完 string_at 之后,使用:
pointer = my_dll.get_str()
some_str = ctypes.string_at(pointer)
# This is windows specific -
# on Posix, one have to load "libc.so" and use "free" from there:
ctypes.cdll.msvcrt.free(pointer)