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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

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

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

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

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

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

import Darwin // or Glibc on Linux

strlen("Hello ?") // → 10

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

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

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

對 C 字符串數組沒有內置支持

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

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

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

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

將 Swift 字符串數組轉換為 C 字符串數組

假設我們想為 posix_spawn

/// 產生一個子進程

///

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

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

現在我們需要將參數數組轉換為 posix_spawn 能夠接收的格式。 這需要幾個步驟:

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

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

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

在緩沖區的末尾添加另一個空字節,表明 C 數組的結尾。

確保緩沖區存在于 posix_spawn 被調用的整個生命周期內。

withArrayOfCStrings 在標準庫中

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

public func withArrayOfCStrings(

_ args: [String],

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

) -> R

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

我們現在可以這樣實現 spawn 函數:

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

這是使用 spawn 函數的樣例:

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

這是執行程序的輸出:

$ 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 中調用它,posix_spawn 會返回一個錯誤,可能是因為 playground 的沙盒不允許生成子進程。因此最好通過命令行創建,或在 Xcode 中創建一個新的命令項目)。

工作原理

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)

}

}

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

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

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

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

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

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

let argsBufferSize = argsOffsets.last!

var argsBuffer: [UInt8] = []

argsBuffer.reserveCapacity(argsBufferSize)

現在可以將 UTF-8 編碼的字節寫入緩沖區,并在每個輸入的字符串后添加一個空字節:

for arg in args {

argsBuffer.append(contentsOf: arg.utf8)

argsBuffer.append(0)

}

此時,我們有一個正確格式的字節數組(UInt8)。我們仍然需要構造指向緩沖區中的元素的指針數組。這就是函數最后一部分的作用:

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

現在,為了構造指針數組,我們為第二行中創建的字符偏移數組做映射,并根據每個偏移量向后移動指針。最后將結果數組中的最后一個元素設置為 nil,用作表示數組結尾的空指針(記得我們之前說的 argsOffset 要比輸入數組包含多一個元素嗎?因此重寫最后一個元素是正確的)。

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

本文由 SwiftGG 翻譯組翻譯,已經獲得作者翻譯授權,最新文章請訪問 http://swift.gg。

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

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

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。