SwiftでCのポインタのインクリメント・デクリメントのコードを書き換える

C/C++でポインタを使ったバッファアクセスを行うときに、頻繁に使用するのがポインタのインクリメントとデクリメントです。ポインタの加算・減算なので、参照位置を変更することができ、連続したバッファで使用して順次アクセスができます。

この記事では、C/C++のポインタのインクリメント・デクリメントを行うコードを、Swiftではどのように書き換えることができるかを解説します。

Cのコード例

ここでは次のようなCのコードを書き換えます。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void fillSequentialNumber(void *buf, int32_t len) {
    uint8_t *p = (uint8_t *)buf;
    uint8_t *end = p + len;

    for (; p != end; p++) {
        *p = (p - (uint8_t *)buf) % 255;
    }
}

void dumpBuf(void *buf, int32_t len) {
    uint8_t *p = (uint8_t *)buf;
    uint8_t *end = p + len;

    for (; p != end; p++) {
        printf("%02x ", *p);
    }
    printf("\n");
}

int main(int argc, const char * argv[]) {
    uint8_t buf[16] = {0};
    fillSequentialNumber(buf, 16);
    dumpBuf(buf, 16);
    return 0;
}

このコードを実行すると、次のように出力されます。

00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 

Swiftでのコード例

Swiftで書き換えたコード例が次のコードです。Swiftではインクリメントとデクリメントがなくなったのと、直接ポインタ操作ができないので、そこをインデックスで表現しています。

import Foundation

func fillSequentialNumber(buffer: UnsafeMutableBufferPointer<UInt8>, length: Int) {
    var cur = buffer.startIndex
    let end = buffer.startIndex.advanced(by: length)
    
    while cur < end {
        buffer[cur] = UInt8((cur - buffer.startIndex) % 255)
        cur = cur.advanced(by: 1)
    }
}

func dump(buffer: UnsafeMutableBufferPointer<UInt8>, length: Int) {
    var cur = buffer.startIndex
    let end = buffer.startIndex.advanced(by: length)
    
    while cur < end {
        print(String(format: "%02x ", buffer[cur]), separator: "", terminator: "")
        cur = cur.advanced(by: 1)
    }
    
    print("")
}

var buf = [UInt8](repeating: 0, count: 16)
let len = buf.count
 
buf.withUnsafeMutableBufferPointer { (ptr) -> Void in
    fillSequentialNumber(buffer: ptr, length: len)
    dump(buffer: ptr, length: len)
}

このコードを実行すると、次のように出力されます。

00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 

他のやり方もあるとは思うのですが、思想を近づけて見たコードです。

バッファ確保

Cのコードではスタックに16バイトの配列で確保しましたが、SwiftではUInt8の配列で確保しました。Dataを使っても同じです。

バッファへのアクセス

UInt8の配列のバッファにアクセスするには、withUnsafeMutableBufferPointerメソッドを使います。バッファにアクセスするコードはクロージャーで渡すのですが、このときに[UInt8]のメソッドを使えないので、先に長さは取得しておきます。

インクリメントとデクリメントのの置き換え

UnsafeMutableBUfferPointerの要素にアクセスするにはインデックスを使います。インクリメントとデクリメントはインデックスに対して行います。

ここでは、インクリメントの代わりにadvanced(by:)を使いましたが、次のように+=演算子を使って書くこともできます。

cur += 1

まとめ

インデックスの位置変更でCのインクリメントやデクリメントと、同じような考え方のコードを書くことができます。ポインタを直接操作しているのとは違うので、どちらかというと、バッファの要素へのランダムアクセスに近いコードです。

それでも、Cのコードの考え方にも近いコードかなと思います。

投稿者プロフィール

林 晃
林 晃アプリ開発者
アールケー開発代表。Appleプラットフォーム向けのアプリ開発が好きなアプリ開発者。アプリの受託開発、技術書執筆、技術指導・セミナー講師。3DCGコンテンツ作成にも取組中です。

基礎から学ぶARKit


「基礎から学ぶARKit」を執筆しました。本書はARKitを使ったARアプリの開発方法を解説した技術書です。

ARKitを使ってARアプリを作るときの流れや基本的なAPIの使い方などをサンプルアプリを作りながら学べます。

詳細

基礎から学ぶMetal


「基礎から学ぶMetal」を執筆しました。本書はMetalを使ってGPUプログラミングを行うための最初のステップを解説するMetalの解説書です。

私が初めてGPUプログラミングを行ったとき、どこから手をつけて、学んでいけば良いのか分からず呆然としました。もし、あのとき、これを教えてくれればという部分を解説しました。本書で解説している部分はMetalの基礎となる部分で、Metalを使うときに必ず触れることになる部分です。

詳細

関連記事

  1. SwiftのUnsafeMutableBufferは[]演算子よりも速…

  2. OpenCVのセットアップ方法(iOSアプリ用)

  3. OpenCVで使われている並列化を見てみる

  4. Cのバイナリデータを確保する処理をSwiftで書く

  5. 【Python】loggingを使ってログを出力する

  6. WKWebViewでBasic認証を行う

最近の著書

  1. 基礎から学ぶ SwiftUI

最近の記事