Python Ctypes字符串转c_char_p/c_void_p后回读异常问题
问题分析与解决思路
核心原因
问题出在Python临时对象生命周期与ctypes内存管理的冲突:
- 直接传入
c_char_p(bytes('hi', 'ascii'))时,bytes('hi', 'ascii')是临时对象,函数调用结束后会被Python垃圾回收机制立即回收,导致c_char_p指向的内存被释放或覆盖,后续回读GSList时内存已无效,自然返回空值。 - 将
bytes存入变量后,变量会持有对象引用,垃圾回收不会回收该内存,c_char_p指向的地址始终有效,因此能正常读取数据。 - 单字符字符串(如
b'h')被Python的字符串驻留机制长期保留在内存中,不会被轻易回收,所以即使作为临时对象传入也能正常读取。
解决思路
1. 保持Python对象引用
将bytes对象存入变量,通过变量持有引用避免被垃圾回收:
from ctypes import * glib = CDLL("libglib-2.0.so") # 保存bytes对象到变量,维持引用 data = b'hi' str_ptr = c_char_p(data) # 构建GSList slist = glib.g_slist_prepend(None, str_ptr) # 回读验证 retrieved_ptr = cast(glib.g_slist_nth_data(slist, 0), c_char_p) print(retrieved_ptr.value) # 输出b'hi'
2. 使用C堆内存分配
用ctypes.create_string_buffer在C堆上分配内存,不受Python垃圾回收影响:
from ctypes import * glib = CDLL("libglib-2.0.so") # 在C堆创建字符串缓冲区,自动处理终止符 str_buf = create_string_buffer(b'hi') slist = glib.g_slist_prepend(None, str_buf) # 回读验证 retrieved_ptr = cast(glib.g_slist_nth_data(slist, 0), c_char_p) print(retrieved_ptr.value) # 输出b'hi'
若需手动释放内存,可导入libc的free函数操作:
libc = CDLL("libc.so.6") libc.free(str_buf) glib.g_slist_free(slist)
3. 手动管理内存(复杂场景适用)
手动分配C堆内存并拷贝字符串,用完后主动释放:
from ctypes import * glib = CDLL("libglib-2.0.so") libc = CDLL("libc.so.6") data = b'hi' # 分配内存并拷贝字符串 mem_ptr = libc.malloc(len(data) + 1) memcpy(mem_ptr, data, len(data)) # 添加字符串终止符 cast(mem_ptr, POINTER(c_char))[len(data)] = b'\x00' slist = glib.g_slist_prepend(None, mem_ptr) # 回读验证 retrieved_ptr = cast(glib.g_slist_nth_data(slist, 0), c_char_p) print(retrieved_ptr.value) # 输出b'hi' # 清理内存 libc.free(mem_ptr) glib.g_slist_free(slist)
内容的提问来源于stack exchange,提问作者jpcgt
相关产品推荐
相关产品推荐

