Swift调用C库:C结构体初始化及char**等指针变量赋值问题
关于Swift调用C库结构体的初始化与赋值问题
我正在尝试从Swift中调用一个老旧的C终端应用/库,已经成功集成源码并完成C到Swift的头文件桥接,代码能编译运行,也能访问C库的所有函数。
C库里有一个args结构体,需要通过接收指针的初始化函数来完成初始化,还要手动给结构体变量赋值:
C结构体定义:
struct args { char ** var1; unsigned char * var2; char * var3; };初始化函数调用:
void init(args * ptr);
现在有两个具体问题:
问题1:这段Swift代码能成功初始化结构体吗?
给出的代码片段:
let Ptr = UnsafeMutablePointer<args>.allocate(capacity: 1) var args = args() Ptr.pointee = args init(Ptr)
解答
这段代码可以完成初始化,但有几个细节需要注意:
UnsafeMutablePointer<args>.allocate(capacity:1)确实分配了足够存放一个args结构体的内存空间;- 创建空的
args()实例再赋值给Ptr.pointee,相当于先把结构体的成员初始化为默认的空指针(C结构体在Swift里默认初始化会把指针设为对应nil的空地址); - 最后调用C的
init(Ptr)函数,只要这个C函数内部会正确处理传入的空指针成员并完成必要的初始化(比如给指针分配内存),整个流程是可行的。
不过要记得,后续使用完这个指针后,必须手动调用deallocate释放内存,避免内存泄漏。
问题2:初始化成功后,如何给var1、var2、var3赋值?
它们在Swift中的映射类型:
var1: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>! var2: UnsafeMutablePointer<UInt8>! var3: UnsafeMutablePointer<Int8>!
需求示例:var1 = {"a", "b"},var2 = {1,2,3},var3 = "a"
分步解决方案
1. 给var3赋值(单个字符串)
Swift字符串转C字符串可以用withCString方法,但如果C库需要长期持有这个字符串,得手动复制内存到堆上:
// 如果C库会持有该字符串,必须手动分配内存复制 let str3 = "a" let cStr3 = str3.withCString { ptr in strdup(ptr) // 复制字符串到堆内存,返回UnsafeMutablePointer<Int8> } Ptr.pointee.var3 = cStr3 // 注意:后续不需要时要调用free(cStr3)释放内存
2. 给var2赋值(UInt8数组)
需要先分配对应长度的内存,再把Swift数组的元素复制进去:
let array2: [UInt8] = [1,2,3] // 分配内存,长度等于数组元素个数 let ptr2 = UnsafeMutablePointer<UInt8>.allocate(capacity: array2.count) // 将数组元素复制到分配好的内存中 ptr2.initialize(from: array2, count: array2.count) Ptr.pointee.var2 = ptr2 // 后续不需要时要调用ptr2.deallocate()释放内存
3. 给var1赋值(字符串数组,对应char**)
这部分是最容易踩坑的,需要按C风格字符串数组的规范来处理:
let array1 = ["a", "b"] // 1. 分配存放指针的内存,长度是数组元素个数+1(C风格字符串数组通常以NULL结尾) let ptr1 = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: array1.count + 1) // 2. 逐个将Swift字符串转成C字符串并赋值 for (index, str) in array1.enumerated() { let cStr = str.withCString { ptr in strdup(ptr) } ptr1[index] = cStr } // 3. 最后一个元素设为NULL,符合C的字符串数组结束规范 ptr1[array1.count] = nil // 4. 赋值给结构体成员 Ptr.pointee.var1 = ptr1 // 后续释放:先逐个释放每个字符串的内存,再释放指针数组的内存 // for i in 0..<array1.count { // if let cStr = ptr1[i] { // free(cStr) // } // } // ptr1.deallocate()
关键注意事项
- 所有手动用
allocate或strdup分配的内存,必须在不需要的时候手动释放,否则会造成内存泄漏; - 一定要确认C的
init函数是否已经帮你分配了某些成员的内存,如果是,就不需要再手动分配,否则会导致重复分配的问题; - C风格字符串数组必须以
nil结尾,很多C函数会依赖这个判断数组的结束位置。
内容的提问来源于stack exchange,提问作者George davies
相关产品推荐
相关产品推荐

