いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-519 (cgoメモ-13)(ポインタ演算)

関連記事

Goメモ-502 (cgoメモ-01)(cgoヘッダ) - いろいろ備忘録日記

Goメモ-506 (cgoメモ-02)(cgoヘッダ) - いろいろ備忘録日記

Goメモ-507 (cgoメモ-03)(C.int) - いろいろ備忘録日記

Goメモ-508 (cgoメモ-04)(C言語の構造体) - いろいろ備忘録日記

Goメモ-509 (cgoメモ-05)(C.CString)(Cの文字列) - いろいろ備忘録日記

Goメモ-510 (cgoメモ-06)(C.GoString)(Cの文字列をGoの文字列へ) - いろいろ備忘録日記

Goメモ-511 (cgoメモ-07)(C.CBytes)([]byteをCのバイト列に) - いろいろ備忘録日記

Goメモ-512 (cgoメモ-08)(C.GoBytes)(Cのバイト列をGoの[]byteへ) - いろいろ備忘録日記

Goメモ-514 (cgoメモ-09)(C.GoStringN)(C.GoStringのサイズ指定版) - いろいろ備忘録日記

Goメモ-515 (cgoメモ-10)([]byteを(void *)へ変換) - いろいろ備忘録日記

Goメモ-516 (cgoメモ-11)([]byteを(char *)へ変換) - いろいろ備忘録日記

Goメモ-518 (cgoメモ-12)(Cのmallocをcgo経由で呼び出し) - いろいろ備忘録日記

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

今回から複数回に渡って cgo についてメモしていこうと思います。

cgo は、文字通りGoからCにアクセスすることが出来るようになるものなのですが、とても便利な反面、結構クセが強いのでメモでも残しておかないとすぐ頭から消えてしまいそうだなって思いました。

Cgo is not Go

という格言があったりするので、Go界隈で標準で推奨されていない技術かもしれません。が、実務ではC言語で作成されたライブラリなどは山のようにあります。んで、プロジェクトの方針でGoで作り直すことも出来ない場合も多々あります。そのような場合に非常に便利です。

これからのサンプルは以下のリポジトリにアップしてありますので、良ければご参考ください。

github.com

今回は cgo で ポインタ演算 について。

これは、cgoに特化した内容ではありませんが、C言語ではポインタ演算が可能であり、それなりに利用されています。

対象的に、Goでは基本的にポインタ演算が禁止されています。

が、unsafeパッケージとuintptrを用いることで、一応可能です。

しかし、これらはGoの世界から大きく離脱した行為となるため、基本的にやるべきではないとされています。(https://stackoverflow.com/a/32701024)

サンプル

main.go

package main

/*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int SKIP_BYTES = 6;

// cgo側でポインタ演算後のアドレスで処理
void p(const void *v) {
   char *m = (char *)v;
   printf("%s\n", m);
}

// 自分でポインタ演算して処理
void p2(const void *v, const int skip) {
   char *m = (char *)v;
   printf("%s\n", m+skip);
}
*/
import "C"
import "unsafe"

func main() {
    var (
        goStr   = "hello Go World"
        cStr    = C.CString(goStr)
        cStrPtr = unsafe.Pointer(cStr)
    )
    defer C.free(cStrPtr)

    C.p(cStrPtr)

    // uintptr に変換することでポインタ演算が可能となる
    // 演算後を再度 unsafe.Pointer にする
    //
    // 以下はメモリアドレスを6バイト進めたポインタを取得している
    var (
        offsetPtr = unsafe.Pointer(uintptr(cStrPtr) + uintptr(C.SKIP_BYTES))
    )

    C.p(offsetPtr)
    C.p2(cStrPtr, C.SKIP_BYTES)
}

Taskfile.yml

# https://taskfile.dev

version: '3'

tasks:
  default:
    cmds:
      - go run main.go

実行

$ task
task: [default] go run main.go
hello Go World
Go World
Go World

参考情報

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。