いろいろ備忘録日記

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

Goメモ-518 (cgoメモ-12)(Cのmallocをcgo経由で呼び出し)

関連記事

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 *)へ変換) - いろいろ備忘録日記

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

概要

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

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

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

Cgo is not Go

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

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

github.com

今回は cgo で malloc を呼び出すやり方について。

cgoからは C.malloc で呼び出すことが可能なのですが、cgoで利用する場合は特殊仕様があって実際にはGo側のラッパー関数が呼び出されます。C言語のmalloc関数はメモリ確保に失敗した場合NULLを返却しますが、このラッパー関数は決してnilを返すことはありません。メモリ確保に失敗した場合は、パニックになります。

以下、ドキュメントの記載です。(https://pkg.go.dev/cmd/cgo)

As a special case, C.malloc does not call the C library malloc directly but instead calls a Go helper function that wraps the C library malloc but guarantees never to return nil. If C's malloc indicates out of memory, the helper function crashes the program, like when Go itself runs out of memory. Because C.malloc cannot fail, it has no two-result form that returns errno.

特殊なケースとして、C.mallocはCライブラリのmallocを直接呼び出すのではなく、CライブラリのmallocをラップするGoヘルパー関数を呼び出しますが、決してnilを返さないことが保証されています。Cのmallocがメモリ不足を示すと、Go自身がメモリ不足になったときのように、ヘルパー関数がプログラムをクラッシュさせる。C.mallocは失敗しないので、errnoを返す2つの結果形式はありません。

サンプル

main.go

package main

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

void p(const void *v) {
   char *m = (char *)v;
   printf("%s\n", m);
}
*/
import "C"
import "unsafe"

func main() {
    var (
        bufPtr unsafe.Pointer
        szBuf  = C.size_t(11)
    )

    // malloc()を呼び出しヒープメモリを確保してもらう.
    // 内部では malloc() をラップしたヘルパー関数が呼び出される.
    bufPtr = C.malloc(szBuf)
    defer C.free(bufPtr)

    C.memset(bufPtr, C.int(0), szBuf)

    //
    // 文字列をコピー
    //
    var (
        cStr    = C.CString("helloworld")
        cStrPtr = unsafe.Pointer(cStr)
    )
    defer C.free(cStrPtr)

    C.memcpy(bufPtr, cStrPtr, szBuf)

    //
    // C側でちゃんと見れるか確認
    //
    C.p(bufPtr)
}

Taskfile.yml

# https://taskfile.dev

version: '3'

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

実行

$ task
task: [default] go run main.go
helloworld

参考情報

Goのおすすめ書籍


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

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