通常情况下,你在 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'))
Output:
<ctypes.LP_c_char object at 0x00000279D7959DC0>
b'abcdefghijkl'