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

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'

问题原因

  1. C风格字符串截断:Python用c_char_p转换指针时,会把内存内容当作C风格字符串处理,遇到第一个值为0的UInt8元素就会截断,导致后续数据丢失。大数组中大概率包含空字节,这是核心问题。
  2. 类型不匹配:你给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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:55:32