如何在ctypes中传回指针?
Posted
技术标签:
【中文标题】如何在ctypes中传回指针?【英文标题】:How to pass pointer back in ctypes? 【发布时间】:2012-12-04 04:46:00 【问题描述】:对ctypes不太了解,最近才开始接触。
我在类 C 的 dll 中有一个简单的函数,它返回一个指向动态生成的字符串的指针。 它工作正常,但是因为我为字符串手动分配了内存,所以我应该在使用后释放它。
我有这样的事情:
extern "C" char* DLL_EXPORT func(const char* str1, const char* str2)
return getSomeString(str1, str2);
// Goal is to call this function correctly from Python.
extern "C" void DLL_EXPORT freeMem(void *mem)
if(mem!=NULL)
delete mem;
但我不知道,如何将接收到的指针传回以在 Python 中删除?
【问题讨论】:
见***.com/questions/7398575/…。 【参考方案1】:通常,您在ctypes
中使用的每个函数都应声明其参数和返回类型,以便 Python 可以检查参数的正确数量和类型,并将 Python 对象参数转换为正确的 C 数据对象。不幸的是,在这种情况下,func
的正常返回值将是 c_char_p
,但 ctypes
试图提供帮助并将 c_char_p
返回值转换为 Python 字符串,从而无法访问原始 C 指针值。相反,您可以将返回类型声明为 POINTER(c_char)
并使用 cast
检索字符串值,从而将返回值保留为可以释放的 LP_c_char
对象。
这是一个例子。请注意,声明正确的 .restype
对于 64 位 Python 尤其重要,因为默认返回类型是 c_int
(32 位)并且可能会截断 64 位指针。此代码已使用 32 位和 64 位版本进行了测试。
test.c
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
API char* func(const char* str1, const char* str2)
size_t len = strlen(str1) + strlen(str2) + 1;
char* tmp = malloc(len);
strcpy_s(tmp, len, str1);
strcat_s(tmp, len, str2);
return tmp;
API void freeMem(void *mem)
free(mem);
test.py
import ctypes as ct
dll = ct.CDLL('./test')
dll.func.argtypes = ct.c_char_p,ct.c_char_p
dll.func.restype = ct.POINTER(ct.c_char)
dll.freeMem.argtypes = ct.c_void_p,
dll.freeMem.restype = None
# Helper function to extract the return value as a Python object
# and always free the pointer.
def freeMem(a,b):
p = dll.func(b'abcdef', b'ghijkl')
print(p)
s = ct.cast(p, ct.c_char_p).value
dll.freeMem(p)
return s
print(freeMem(b'abcdef', b'ghijkl'))
输出:
<ctypes.LP_c_char object at 0x00000279D7959DC0>
b'abcdefghijkl'
【讨论】:
【参考方案2】:你在正确的轨道上。
// TestDLL.cpp
#include <string.h> // strcpy
extern "C" __declspec(dllexport) char* stringdup(const char* str)
char* p = new char[strlen(str)+1];
strcpy(p,str);
return p;
// if you have no good reason to use void*, use the type
// you've allocated. while it usually works for built-in
// types, it wouldn't work for classes (it wouldn't call
// the destructor)
extern "C" __declspec(dllexport) void stringfree(char* ptr)
// you don't need to check for 0 before you delete it,
// but if you allocate with new[], free with delete[] !
delete [] ptr;
在python中:
# Test.py
import ctypes
lib = ctypes.cdll.TestDLL
# this creates a c-style char pointer, initialized with a string whose
# memory is managed by PYTHON! do not attempt to free it through the DLL!
cstr = ctypes.c_char_p("hello ctypes")
# call the dll function that returns a char pointer
# whose memory is managed by the DLL.
p = lib.stringdup(cstr)
# p is just an integer containing the memory address of the
# char array. therefore, this just prints the address:
print p
# this prints the actual string
print ctypes.c_char_p(p).value
# free the memory through the DLL
lib.stringfree(p)
【讨论】:
stringdup.restype
需要设置为指针类型,例如 POINTER(c_char)
,因为默认结果类型是 32 位 int
。要获取 Python 字符串,可以使用 cast(p, c_char_p).value
或 c_char_p.from_buffer(p).value
。【参考方案3】:
对我来说
#cdll.execute.restype = ctypes.c_char_p # failed to free
cdll.execute.restype = ctypes.POINTER(ctypes.c_char) # worked
p = cdll.execute('...')
print(
ctypes.cast(p, ctypes.c_char_p).value.decode('utf-8')
)
cdll.free_pointer(p)
【讨论】:
以上是关于如何在ctypes中传回指针?的主要内容,如果未能解决你的问题,请参考以下文章
Python 和 ctypes:如何正确地将“指针对指针”传递给 DLL?
你如何从 sip.voidptr (QImage.constBits()) 到 ctypes void 或 char 指针?