You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 15:15:42