Swift 提供完整的 C 指標互操作支援,自動處理常見的標量、陣列、字串轉換。本文詳細說明 inout 參數、陣列指標、字串指標轉換規則與安全性邊界。
C 函式常用指標回傳多個值,Swift 以 inout + & 對應:
// C: void getColor(float *r, float *g, float *b, float *a);
// Swift 對應:
var r: Float = 0, g: Float = 0, b: Float = 0, a: Float = 0
getColor(&r, &g, &b, &a) // 自動建立臨時指標傳遞
// NSError 雙重指標慣用法
var error: NSError?
let success = fileManager.contentsOfDirectoryAtPath("/usr/bin", error: &error)
if !success { print(error) }
Swift 陣列可直接轉為 C 指標,支援常數/可變指標:
import Accelerate
let a: [Float] = [1, 2, 3, 4]
let b: [Float] = [0.5, 0.25, 0.125, 0.0625]
var result: [Float] = [0, 0, 0, 0]
// vDSP_vadd(const float *A, stride, const float *B, stride, float *C, stride, length)
vDSP_vadd(a, 1, b, 1, &result, 1, 4)
// result = [1.5, 2.25, 3.125, 4.0625]
// 關鍵點:
// - a, b 為常數陣列 → 自動轉為 UnsafePointer (const float*)
// - &result 為可變陣列 → 自動轉為 UnsafeMutablePointer (float*)
// - 步長 stride=1 表示連續陣列
Swift String 可隱式轉為 UnsafePointer (即 const char*):
// C: int puts(const char *s);
// C: int open(const char *path, int flags, mode_t mode);
// C: ssize_t write(int fd, const void *buf, size_t count);
puts("Hello from libc") // 直接傳遞字串字面值
let fd = open("/tmp/test.txt", O_WRONLY | O_CREAT, 0o666)
if fd >= 0 {
let text = "Hello World"
write(fd, text, strlen(text)) // text 自動轉為 const char*
close(fd)
}
編碼:Swift 字串以 UTF-8 編碼傳遞,以 \0 結尾。
⚠️ 以下情況轉換後的指標不安全/無效:
| 情況 | 風險 | 對策 |
|---|---|---|
| C 函式保存指標供後續使用 | 指標僅在呼叫期間有效,呼叫結束即失效 | 在 Swift 端保存資料副本,或用全域/靜態變數 |
| 多次傳遞同一變數產生不同指標 | 每次轉換產生新的臨時緩衝區 | 改用 withUnsafeMutablePointer 手動控制生命週期 |
| 陣列/字串越界存取 | C 函式不檢查邊界 | 呼叫前確保容量足夠,或用 UnsafeBufferPointer 檢查 |
| 非同步/逃逸閉包中使用指標 | 指標可能指向已釋放記憶體 | 用 withExtendedLifetime 延長生命週期 |
需精確控制生命週期時,使用 withUnsafe...Pointer 系列:
var value = 42
withUnsafeMutablePointer(to: &value) { ptr in
// ptr: UnsafeMutablePointer
// 此閉包內 ptr 保證有效
c_function(ptr)
}
// 閉包結束後 ptr 失效,value 仍安全
與 C 結構體對應時,使用 @frozen 或確保屬性順序:
// C: struct Point { float x, y; };
@frozen struct Point {
var x: Float
var y: Float
}
// 記憶體佈局與 C 完全一致,可直接轉指標傳遞
// 安全:全域變數位址固定
var globalContext = MyContext()
let kvoContext = withUnsafePointer(to: &globalContext) { $0 }
// kvoContext 可作為永久唯一指標用於 KVO context
inout + &,最安全簡潔。&array,注意 stride 與連續性。withUnsafe...Pointer 明確生命週期。