Go unsafe 包的使用

unsafe包

golang是一種靜態的強類型的語言,全部的類型都是不能隨意轉換的,Go語言是不容許兩個指針類型進行轉換的。go官方是不推薦使用unsafe的操做由於它是不安全的,它繞過了golang的內存安全原則,容易使你的程序出現莫名其妙的問題,不利於程序的擴展與維護。可是在不少地方倒是很實用。在一些go底層的包中unsafe包被很頻繁的使用。golang

unsafe 定義

package unsafe
//ArbitraryType僅用於文檔目的,實際上並非unsafe包的一部分,它表示任意Go表達式的類型。
type ArbitraryType int
//任意類型的指針,相似於C的*void
type Pointer *ArbitraryType
//肯定結構在內存中佔用的確切大小
func Sizeof(x ArbitraryType) uintptr
//返回結構體中某個field的偏移量
func Offsetof(x ArbitraryType) uintptr
//返回結構體中某個field的對其值(字節對齊的緣由)
func Alignof(x ArbitraryType) uintptr

官方中定義了四個描述:數組

  1. 任何類型的指針均可以被轉化爲Pointer
  2. Pointer能夠被轉化爲任何類型的指針
  3. uintptr能夠被轉化爲Pointer
  4. Pointer能夠被轉化爲uintptr

unsafe的使用

類型轉換

使用unsafe能夠實現類型的轉換,下面的例子能夠看到i是一個int類型,使用unsafe.Pointer轉換成float64而且還修改了指針對應的值。安全

func main() {
    i := 10
    ip := &i

    fp := (*float64)(unsafe.Pointer(ip))

    *fp = *fp * 3

    fmt.Println(i)
}

// 結果: 30

可是使用起來要十分的當心,若是使用不當會引起錯誤。能夠舉一個例子:函數

func main() {
    i := 10
    ip := &i

    fp := (*string)(unsafe.Pointer(ip))

    *fp = "a"

    fmt.Println(i)
    // 結果:19678090
}

上面的誤操做就是把int類型轉成了string,而且修改了值致使結果出現了錯誤,而且這種錯誤ui

根據位移獲取、修改對象的字段

利用unsafe的Pointer和Offsetof函數,能夠獲取對象的屬性,而且能夠修改對象的屬性指針

type Student struct {
    Name string
    Age  int
}

func main() {
    s := Student{}
    s.Name = "Peter"
    s.Age = 33

    pStudent := unsafe.Pointer(&s)
    // 整個對象轉換成指針,默認是獲取第一個屬性
    name := (*string)(unsafe.Pointer(pStudent))
    fmt.Println("name:", *name)
    // 利用Offsetof獲取age屬性的偏移量獲取屬性
    age := (*int)(unsafe.Pointer(uintptr(pStudent) + unsafe.Offsetof(s.Age)))
    fmt.Println("age:", *age)
    
    // 修改指針的值
    *name = "Mary"
    *age = 20
    fmt.Println(s)
}

獲取私有變量

能夠經過unsafe獲取私有變量的值,也能夠修改值。這個操做跟上面的獲取值是同樣的簡單的例子以下:code

type Teacher struct {
    name string
    age  int
}

func main() {
    t := Teacher{"ttt", 20}

    pt := unsafe.Pointer(&t)
    name := (*string)(unsafe.Pointer(pt))
    fmt.Println("name:", *name)
}

根據sizeof函數獲取、修改

利用unsafe中的sizeof函數獲取數組的值對象

func main() {
    array := []int{0, 1, -2, 3, 4}
    pointer := &array[0]
    fmt.Print(*pointer, " ")
    memoryAddress := uintptr(unsafe.Pointer(pointer)) + unsafe.Sizeof(array[0])
    for i := 0; i < len(array)-1; i++ {
        pointer = (*int)(unsafe.Pointer(memoryAddress))
        fmt.Print(*pointer, " ")
        memoryAddress = uintptr(unsafe.Pointer(pointer)) + unsafe.Sizeof(array[0])
    }
}
結果:0 1 -2 3 4
相關文章
相關標籤/搜索