Swift通过@_cdecl向Python传递大字节数组数据丢失问题
Swift与Python传递大UInt8数组时数据丢失问题
我实现了Swift和Python间传递UInt8数组的函数,小数组(20个元素)传输正常,但大数组(21576个元素)传输时Python端只能收到少量字节。已确认Swift侧UnsafeMutablePointer<UInt8>内的数据正确,但Python端出现数据丢失。
Swift代码
@_cdecl("getPointer") public func getPointer() -> UnsafeMutablePointer<UInt8>{ let arr: Array<UInt8> =[1,2,3.....] // 这里是大数组 if let buffer = buffer { buffer.deallocate() buffer.deinitialize(count: arr.count) } buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: arr.count * MemoryLayout<UInt8>.stride) buffer!.initialize(from: arr, count: arr.count) return buffer! }
Python代码
native_lib = ctypes.CDLL('./libH264_decoder') native_lib.getPointer.restype = ndpointer(dtype=ctypes.c_uint8) cont = cast(native_lib.getPointer(), c_char_p).value
Python端返回结果
b'\x1cri\x1aVL\xa4q\xfc\xa7\xaezb\x83HC\x94\xb4#\xde?x\xdb\xb1\xd3\x1d\x07\xb5@\xc8\x85\x0eP\xaa\x9ew\x03\x93\xfe8\xa6\x97D\xca\xc6\xcc'
问题原因
- C风格字符串截断:Python用
c_char_p转换指针时,会把内存内容当作C风格字符串处理,遇到第一个值为0的UInt8元素就会截断,导致后续数据丢失。大数组中大概率包含空字节,这是核心问题。 - 类型不匹配:你给
getPointer设置的返回类型是ndpointer,但后续又强制转成c_char_p,类型冲突会引发不可预期的内存读取行为。
解决方案
方案1:传递指针+数组长度,按长度读取
修改Swift函数,同时返回指针和数组实际长度:
// 定义结构体用于封装指针和长度 struct BufferInfo { var pointer: UnsafeMutablePointer<UInt8> var length: Int } @_cdecl("getBufferInfo") public func getBufferInfo() -> BufferInfo { let arr: Array<UInt8> = [1,2,3.....] // 大数组 // 修正内存释放顺序:先销毁元素再释放内存,且用旧buffer的长度 if let oldBuffer = buffer, let oldCount = bufferCount { oldBuffer.deinitialize(count: oldCount) oldBuffer.deallocate() } let newCount = arr.count buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: newCount) buffer!.initialize(from: arr, count: newCount) bufferCount = newCount // 新增全局变量保存当前buffer长度 return BufferInfo(pointer: buffer!, length: newCount) }
Python端按指定长度读取完整内存:
import ctypes from ctypes import cdll, c_uint8, c_int, POINTER # 定义对应Swift的结构体 class BufferInfo(ctypes.Structure): _fields_ = [("pointer", POINTER(c_uint8)), ("length", c_int)] native_lib = cdll.LoadLibrary('./libH264_decoder') native_lib.getBufferInfo.restype = BufferInfo # 获取指针和长度,读取完整数据 info = native_lib.getBufferInfo() cont = bytes(info.pointer[:info.length])
方案2:用numpy的ndpointer直接读取(需提前知道数组长度)
如果依赖numpy,可以直接指定数组形状,让Python按固定长度读取内存:
import ctypes import numpy as np native_lib = ctypes.CDLL('./libH264_decoder') # 明确指定数组长度为21576 native_lib.getPointer.restype = np.ctypeslib.ndpointer(dtype=np.uint8, shape=(21576,)) # 直接获取完整数组 arr = native_lib.getPointer() cont = arr.tobytes()
额外注意
Swift代码中内存释放顺序错误:当前先调用deallocate()再deinitialize(),会导致访问已释放内存的未定义行为,必须先销毁元素(deinitialize)再释放内存(deallocate),且要使用旧buffer的实际长度,不能用新数组的长度。
内容的提问来源于stack exchange,提问作者PrettyLittleHorse
相关产品推荐
相关产品推荐

