go言語ポインタタイプの使用

3136 ワード

go言語のポインタタイプ
簡単に言えばgo言語のポインタタイプはC/C++のポインタタイプと同じですが、セキュリティの考慮を除いて、go言語には以下のような制限が追加されています.
  • 異なるタイプのポインタは互いに変換できません.例えば、*int、int 32、int 64
  • などです.
  • 任意の一般的なポインタタイプ*Tとuintptrの間で
  • を互いに変換することはできない.
  • ポインタ変数は演算できません.例えば、C/C++の中の++、--演算
  • 例えば
    package main
    
    import ( "fmt" )
    
    func main() {
       i1 := int(1)
       i2 := int32(1)
       i3 := int64(1)
    
       p1 := &i1
       p2 := &i2
       p3 := &i3
    
       p1 = (* int)(p2)
       p1 = (* int)(p3)
    
       p2 = (* int32)(p1)
       p2 = (* int32)(p3)
    
       p3 = (* int64)(p1)
       p3 = (* int64)(p2)
    
       p1 = p1 + 8
    
       fmt.Printf("p1=%p,p2=%p,p3=%p
    ", p1, p2, p3); }

    goコンパイラは、すべてのポインタタイプ変換をエラーとし、ポインタの演算をエラーとします.以下のようにします.
    $ go build main.go
    # command-line-arguments
    ./main.go:14: cannot convert p2 (type *int32) to type *int
    ./main.go:15: cannot convert p3 (type *int64) to type *int
    ./main.go:17: cannot convert p1 (type *int) to type *int32
    ./main.go:18: cannot convert p3 (type *int64) to type *int32
    ./main.go:20: cannot convert p1 (type *int) to type *int64
    ./main.go:21: cannot convert p2 (type *int32) to type *int64
    ./main.go:23: invalid operation: p1 + 8 (mismatched types *int and int)
    

    しかしgo言語はunsafeも提供しています.Pointerパッケージは、その助けのもとで、すべての操作が可能になりました.ただし、これらの操作は安全ではありません.unsafeパッケージに提供される関数がGoタイプのシステムとメモリ管理のセキュリティメカニズムを破るため、ユーザーは非常に注意する必要があります.これは、そのパッケージ名がunsafeと呼ばれている理由です.
    unsafeパッケージ
    パッケージunsafeは、次の2つの重要なポインタに関連する機能を提供します.
  • の任意のタイプのポインタをunsafeに変換することができる.Pointerタイプ、逆も
  • のuintptr値をunsafeに変換することができる.Pointerタイプ、逆も
  • 前述したgo言語のポインタ使用の制限と比較して、この2つの拡張は、ポインタ使用の制限に対して拡張をカスタマイズします.
    第1条により、ポインタタイプ間の変換が可能となり、まず元のポインタタイプをunsafeに変換する.Pointer、それからunsafe.Pointerはターゲットポインタタイプに変換されます.さらに第2条により、ポインタの演算が可能となり、まず元のポインタをunsafeに変換する.Pointer、unsafe.Pointerはuintptrに変換する、数値の演算を行い、演算後の値をunsafeに変換する.最後にunsafeをPointerは対応する元のデータポインタに戻ります.
    ここでunsafeを補足します.Pointerとuintptrの2つのタイプは、2つの文を単独で説明します.
  • unsafe.Pointerは1つのポインタタイプで、指す値は解析されず、C/C++の中の(void*)に似ていて、これが1つのポインタであることを説明しているだけで、何を指しているのか分かりません.
  • uintptrは、ポインタタイプデータを格納するのに十分な幅の整数タイプである.それは整数クラスタイプである以上、もちろん演算することができます.

  • 使用例を挙げる
    package main
    
    import (
      "fmt"
      "unsafe"
    )
    
    func main() {
       var ii [4]int = [4]int{ 11, 22, 33, 44 }
    
       var p0 * int          = &ii[0]               // p0 point to first element
       var p1 unsafe.Pointer = unsafe.Pointer(p0)   // convert (* int) to unsafe.Pointer
       var p2 uintptr        = uintptr(p1)          // convert unsafe.Pointer to uintptr
       p2 += 8                                      // computing uintptr with plus 8, i.e, the next element address
       var p3 unsafe.Pointer = unsafe.Pointer(p2)   // convert uintptr back to unsafe.Pointer
       var p4 * int64        = (* int64)(p3)        // convert unsafe.Pointer to another type pointer, (* int64)
    
       fmt.Printf("*p0=%d,*p4=%d
    ", *p0, *p4); }

    この例では、int配列を定義し、最初の要素を指すポインタを定義し、このポインタを演算して次の要素を指し、最後にint 64の形式で次の要素の値を印刷します.
    $ go build && ./main 
    *p0=11,*p4=22
    

    unsafeについてPointerの詳細
    公式文書を参照してくださいhttps://golang.org/pkg/unsafe/#Pointer