3

我正在使用 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 解释器中超出范围时,该内存会被垃圾收集,还是我需要手动释放它?

4

1 回答 1

5

string_at在新的内存位置创建一个新的 Python 字符串,完全独立于调用它的内存位置。

Python 或 ctypes 无法猜测您的本机代码返回了什么 - 就它而言,它只是一个数字(在这种情况下恰好是一个有效的指针)。

因此,经验法则是:如果您编写分配内存的 C 代码,您还应该编写等效的 C 代码来取消分配它 - 并调用从您的 ctypes 使用 Python 代码中释放 C 代码。

对于像这样的快速脚本和示例,由于您知道它是一个简单的分配字符串,您可以通过使用 ctypes 调用系统free函数直接从 Python 端释放它。

也就是说,将返回的指针存储在 Python var 中:(您可能会将其转换为正确的 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)
于 2013-02-26T03:31:32.367 回答