Python ctypes.string_at 释放行为

4 投票
1 回答
2701 浏览
提问于 2025-04-17 17:08

我正在使用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)

撰写回答