Edited at

SwiftでCの配列を使う

More than 5 years have passed since last update.

Swiftの時代になっても、既存のAPIとの互換性とのために、Cの配列をやりとりするAPIが残っています。


例えば

APIリファレンスで次のようなAPIを調べてみると…

void CGContextSetFillColor ( CGContextRef context, const CGFloat components[] );

func CGContextSetFillColor(_ c: CGContext!,

_ components : CConstPointer<CGFloat>)

引数componentsはObjective-CではCGFloatの配列なのに、SwiftのCConstPointer<CGFloat>って何だよって感じですよね。


型の対応

基本的には、Swiftで配列を作って渡せばいいようになっていますが、ちょっと注意が必要な場合もあります。

まずは、公式ドキュメントより、Cの型とSwiftの型との対応を見てみましょう。

Cの型
Swiftの型

const void *
CConstVoidPointer

void *
CMutableVoidPointer

const Type *
CConstPointer<Type>

Type *
CMutablePointer<Type>


CMutablePointer<Type>

Cの型はType *です。あらかじめ配列のメモリを確保して、呼び出した関数がそこに文字列を入れてくれるような関数などを思い浮かべてください。

CMutablePointer<Type>の場合は、配列の変数名の先頭に "&" を付けて渡します。

func takesAMutablePointer(x: CMutablePointer<Float>) { /*...*/ }

var a: Float[] = [1.0, 2.0, 3.0]
takesAMutablePointer(&a)

NSString

func getCString(_ buffer: CMutablePointer<CChar>,

maxLength maxBufferCount: Int,
encoding encoding: UInt) -> Bool

というAPIを使って、もうちょっと具体的な例を見てみましょう。

let str = "Hello, Swift"

let strLength = Int(countElements(str))
// 文字列の長さ+1の長さの配列を作成
var charArray = CChar[](count: strLength + 1, repeatedValue: 0)
// 配列を渡してCCharを取得
(str as NSString).getCString(&charArray, maxLength: strLength + 1, encoding: NSUTF8StringEncoding)

charArray //[72, 101, 108, 108, 111, 44, 32, 83, 119, 105, 102, 116, 0]


CConstPointer<Type>

Cの型はconst Type *です。Cでconstが付いている配列は値が変更されません。

CConstPointer<Type>の場合は、変数名に何も付けずにそのまま渡します。

func takesAConstVoidPointer(x: CConstVoidPointer)  { /* ... */ }

var a: Float[] = [1.0, 2.0, 3.0]
takesAConstVoidPointer(a)
takesAConstVoidPointer([1.0, 2.0, 3.0])

NSString

class func stringWithCharacters(_ chars: CConstPointer<unichar>,

length length: Int) -> Self!

というAPIを使って、もうちょっと具体的な例を見てみましょう。

let unicharArray:unichar[] = [72, 101, 108, 108, 111, 44, 32, 83, 119, 105, 102, 116]

let len = unicharArray.count
let s = NSString.stringWithCharacters(unicharArray, length: len) // "Hello, Swift"


補足

ちなみに、CでNULLポインタになるようにしたいときは、Swiftではnilを渡せばOKです。