日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言字符串传给swift,如何把字符串数组从 Swift 传递给 C

發(fā)布時間:2025/3/12 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言字符串传给swift,如何把字符串数组从 Swift 传递给 C 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者:Natasha The Robot,原文鏈接,原文日期:2016-10-27

譯者:BigbigChai;校對:walkingway;定稿:CMB

Swift 允許我們將原生的字符串直接傳遞給一個接受 C String(即 char *)的 C API。 比如說,你可以在 Swift 里調(diào)用 strlen 函數(shù),如下所示:

import Darwin // or Glibc on Linux

strlen("Hello ?") // → 10

雖然在 Swift 中,const char * 參數(shù)是作為 UnsafePointer ! 導(dǎo)入的,但這的確可行。 Swift 導(dǎo)入的 strlen 函數(shù)的完整類型定義如下:

func strlen(_ __s: UnsafePointer!) -> UInt

類型檢查器能夠 將 String 值傳遞給一個 UnsafePointer 或 UnsafePointer 參數(shù) 。在此過程中,編譯器隱式地創(chuàng)建了一個緩沖區(qū),它包含一段以 UTF-8 編碼null 結(jié)束的字符串,并傳回一個指向緩沖區(qū)的指針給函數(shù)。

對 C 字符串數(shù)組沒有內(nèi)置支持

Swift 處理單個 char * 參數(shù)的方式非常簡便。但是,一些 C 函數(shù)接收字符串數(shù)組(一個 char * 或 char * [])作為參數(shù),而 Swift 對將 [String] 傳遞給一個 char * 參數(shù)并沒有內(nèi)置支持。

一個實用的例子是子進程啟動時的 posix_spawn 函數(shù)。 posix_spawn 的最后兩個參數(shù)(argv 和 envp)是用于傳遞新進程的參數(shù)和環(huán)境變量的字符串數(shù)組。文檔中是這么說明的:

argv(和 envp)是指向以 null 結(jié)尾的字符串數(shù)組指針,數(shù)組元素指向以 null 結(jié)束的字符串。

Swift 將這些參數(shù)中 C 類型的 char * const argv [] 轉(zhuǎn)換為難以處理的 UnsafePointer ?>!,感嘆號表示 對可選值隱式解包 ,告訴我們 API 這里的參數(shù)不能為空,即 Swift 不知道函數(shù)是否接受傳遞 NULL(在這種情況下外層 UnsafePointer 將為可選值)。我們必須參考文檔來回答這個問題。在本示例中,文檔明確聲明了 argv 必須至少包含一個元素(生成程序的文件名)。 envp 可以為 NULL ,表示它將繼承其父進程的環(huán)境。

將 Swift 字符串數(shù)組轉(zhuǎn)換為 C 字符串數(shù)組

假設(shè)我們想為 posix_spawn

/// 產(chǎn)生一個子進程

///

/// - Returns: A pair containing the return value of `posix_spawn` and the pid of the spawned process.

func spawn(path: String, arguments: [String]) -> Int32

現(xiàn)在我們需要將參數(shù)數(shù)組轉(zhuǎn)換為 posix_spawn 能夠接收的格式。 這需要幾個步驟:

以 UTF-8 編碼字符串元素。

為每個 UTF-8 編碼的字符串的末尾添加一個空字節(jié)。

將所有 UTF-8 編碼的、以空字節(jié)結(jié)尾的字符串拷貝到一個緩沖區(qū)中。

在緩沖區(qū)的末尾添加另一個空字節(jié),表明 C 數(shù)組的結(jié)尾。

確保緩沖區(qū)存在于 posix_spawn 被調(diào)用的整個生命周期內(nèi)。

withArrayOfCStrings 在標準庫中

Swift 團隊也需要使用這個功能來運行標準庫的單元測試,因此標準庫的源也包括一個名為 withArrayOfCStrings 的函數(shù)。現(xiàn)在這是一個私有函數(shù),不公開暴露給 stdlib 使用者(雖然它被聲明為 public,大概因為不這么做的話單元測試無法看到它)。但這個函數(shù)依然對我們可見。這是該函數(shù)的接口:

public func withArrayOfCStrings(

_ args: [String],

_ body: ([UnsafeMutablePointer?]) -> R

) -> R

它具有與 withUnsafePointer 及其變體相同的形式:它的結(jié)果類型 R 是一個泛型,并且接收一個閉包作為參數(shù)。其思想是,在將字符串數(shù)組轉(zhuǎn)換為 C 數(shù)組之后, withArrayOfCStrings 調(diào)用閉包,傳遞 C 數(shù)組,并將閉包的返回值轉(zhuǎn)發(fā)給其調(diào)用者。這使得 withArrayOfCStrings 函數(shù)完全控制它自己創(chuàng)建緩沖區(qū)的生命周期。

我們現(xiàn)在可以這樣實現(xiàn) spawn 函數(shù):

/// Spawns a child process.

///

/// - Returns: A pair containing the return value of `posix_spawn` and the pid of the spawned process.

func spawn(path: String, arguments: [String]) -> (retval: Int32, pid: pid_t) {

// Add the program's path to the arguments

let argsIncludingPath = [path] + arguments

return withArrayOfCStrings(argsIncludingPath) { argv in

var pid: pid_t = 0

let retval = posix_spawn(&pid, path, nil, nil, argv, nil)

return (retval, pid)

}

}

為什么這是可行的呢?能注意到 withArrayOfCStrings 的閉包參數(shù)的類型為 ([UnsafeMutablePointer?]) -> R 。參數(shù)類型 [UnsafeMutablePointer ?] 看起來與 posix_spawn 要求的 UnsafePointer ?>! 并不兼容,但其實是兼容的。CChar 只是 Int8 的別名。再者,正如 Swift 對于傳遞給 C 的字符串會有特殊處理,編譯器隱式地將原生 Swift 數(shù)組傳遞給接收 UnsafePointer 參數(shù)的 C 函數(shù)。因此我們可以將數(shù)組直接傳遞給 posix_spawn,只要它的元素類型與指針指向元素的類型相匹配。

這是使用 spawn 函數(shù)的樣例:

let (retval, pid) = spawn(path: "/bin/ls", arguments: ["-l", "-a"])

這是執(zhí)行程序的輸出:

$ swift spawn.swift

posix_spawn result: 0

new process pid: 17477

total 24

drwxr-xr-x 4 elo staff 136 Oct 27 17:04 .

drwx---r-x@ 41 elo staff 1394 Oct 24 20:12 ..

-rw-r--r--@ 1 elo staff 6148 Oct 27 17:04 .DS_Store

-rw-r--r--@ 1 elo staff 2342 Oct 27 15:28 spawn.swift

(注意,如果你在 playground 中調(diào)用它,posix_spawn 會返回一個錯誤,可能是因為 playground 的沙盒不允許生成子進程。因此最好通過命令行創(chuàng)建,或在 Xcode 中創(chuàng)建一個新的命令項目)。

工作原理

public func withArrayOfCStrings(

_ args: [String], _ body: ([UnsafeMutablePointer?]) -> R

) -> R {

let argsCounts = Array(args.map { $0.utf8.count + 1 })

let argsOffsets = [ 0 ] + scan(argsCounts, 0, +)

let argsBufferSize = argsOffsets.last!

var argsBuffer: [UInt8] = []

argsBuffer.reserveCapacity(argsBufferSize)

for arg in args {

argsBuffer.append(contentsOf: arg.utf8)

argsBuffer.append(0)

}

return argsBuffer.withUnsafeMutableBufferPointer {

(argsBuffer) in

let ptr = UnsafeMutableRawPointer(argsBuffer.baseAddress!).bindMemory(

to: CChar.self, capacity: argsBuffer.count)

var cStrings: [UnsafeMutablePointer?] = argsOffsets.map { ptr + $0 }

cStrings[cStrings.count - 1] = nil

return body(cStrings)

}

}

讓我們逐行解說。第一行為輸入的字符串創(chuàng)建一個 UTF-8 編碼的字符計數(shù)(加上為空的終止標識的一字節(jié))的數(shù)組:

let argsCounts = Array(args.map { $0.utf8.count + 1 })

下一行讀取這些字符計數(shù),并計算每個輸入字符串的字符偏移量,即每個字符串將在緩沖區(qū)中的開始位置。第一個字符串當然將被定位在偏移量為零的地方,并通過累積字符計數(shù)來計算后續(xù)偏移量:

let argsOffsets = [ 0 ] + scan(argsCounts, 0, +)

代碼使用一個名為 scan 的幫助函數(shù),它定義在同一個文件里。注意,argsOffsets 包含的元素數(shù)量比 argsCounts 多一個。因為 argsOffsets 的最后一個元素是最后一個輸入字符串之后的偏移量,即所需的緩沖區(qū)的大小。

下一步是創(chuàng)建一個字節(jié)數(shù)組(元素類型為 UInt8)用作緩沖區(qū)。由于緩沖區(qū)會自動增長,因此調(diào)用 reserveCapacity 不是必要的。但如果在開始時能事先知道的所需容量并保留的話,可以避免重復(fù)的分配行為:

let argsBufferSize = argsOffsets.last!

var argsBuffer: [UInt8] = []

argsBuffer.reserveCapacity(argsBufferSize)

現(xiàn)在可以將 UTF-8 編碼的字節(jié)寫入緩沖區(qū),并在每個輸入的字符串后添加一個空字節(jié):

for arg in args {

argsBuffer.append(contentsOf: arg.utf8)

argsBuffer.append(0)

}

此時,我們有一個正確格式的字節(jié)數(shù)組(UInt8)。我們?nèi)匀恍枰獦?gòu)造指向緩沖區(qū)中的元素的指針數(shù)組。這就是函數(shù)最后一部分的作用:

return argsBuffer.withUnsafeMutableBufferPointer {

(argsBuffer) in

let ptr = UnsafeMutableRawPointer(argsBuffer.baseAddress!).bindMemory(

to: CChar.self, capacity: argsBuffer.count)

var cStrings: [UnsafeMutablePointer?] = argsOffsets.map { ptr + $0 }

cStrings[cStrings.count - 1] = nil

return body(cStrings)

}

我們利用 withUnsafeMutableBufferPointer 獲得數(shù)組,其元素表示指向緩沖區(qū)的指針。內(nèi)部閉包的第一行代碼通過 UnsafeMutableRawPointer 將元素指針的類型從 UnsafeMutablePointer 轉(zhuǎn)換為 UnsafeMutablePointer 。 (從 Swift 3.0 開始,你不能直接在類型化的指針之間進行轉(zhuǎn)換,你必須首先轉(zhuǎn)換成 Unsafe[Mutable] RawPointer 。)這段代碼的可讀性不是很好,但對我們來說這行之后的內(nèi)容才是重要的。本地 ptr 變量是指向緩沖區(qū)中的第一個字節(jié)的 UnsafeMutablePointer。

現(xiàn)在,為了構(gòu)造指針數(shù)組,我們?yōu)榈诙兄袆?chuàng)建的字符偏移數(shù)組做映射,并根據(jù)每個偏移量向后移動指針。最后將結(jié)果數(shù)組中的最后一個元素設(shè)置為 nil,用作表示數(shù)組結(jié)尾的空指針(記得我們之前說的 argsOffset 要比輸入數(shù)組包含多一個元素嗎?因此重寫最后一個元素是正確的)。

最后,我們可以調(diào)用從調(diào)用者傳遞過來的閉包,傳遞指向 C 字符串的指針數(shù)組。

本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權(quán),最新文章請訪問 http://swift.gg。

注意,由于上面的 emoji 是以 UTF-8 格式傳遞的,它在 strlen 函數(shù)里會占用四個“字符“。 ?

在這里使用了 posix_spawn 作為簡單的例子來講解。但在生產(chǎn)代碼中,應(yīng)該使用 Foundation 框架里更高級的 Process 類(née NSTask)來實現(xiàn)。 ?

總結(jié)

以上是生活随笔為你收集整理的c语言字符串传给swift,如何把字符串数组从 Swift 传递给 C的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。