上一篇文章介紹了rust 的unsafe的使用,突然想起來go 裡面也有類似的unsafe 裸指標操作。也是透過unsafe的package。先看一個demo
//定義一個結構體,包含一個string和int64type Test struct { str string num int64}func main() { t := Test{str: "hello", num: 1} tp := unsafe.Pointer(&t) //獲取物件的通用指標 tsp := (*string)(tp) //將通用指標轉化為字串型別的指標 *tsp = "world" //修改指標的內容 tip := (*int64)(unsafe.Pointer(uintptr(tp) + unsafe.Offsetof(t.num))) //獲取num的地址,並轉化為int64指標 *tip = 2 //修改num的值 fmt.Printf("t.str: %s, t.num: %d", t.str, t.num)}
上面程式輸出的內容是
t.str: world, t.num: 2
可以順利透過指標修改底層資料。我們看一下test底層資料結構
test 有兩個部分組成,一個是字串的指標,一個是num,其中字串的記憶體結構是
type StringHeader struct { Data uintptr Len int}
那麼整個記憶體的佈局就是
可以看到tp 指標指向了結構體的地址,當把tp 轉化為字串指標的時候,相當於把整個Test 結構體轉化為字串了,於是便可以修改字串的內容了。後續修改num的原理也是類似的。但這種操作並非一直安全,看下面的例子
type Test struct { age uint8 num uint8}func main() { t := Test{age: 2, num: 10} tp := unsafe.Pointer(&t) tsp := (*int64)(tp) //強行轉化為int64 *tsp = 40000 fmt.Printf("t.age: %d, t.num: %d", t.age, t.num)}
你們先猜一下這個程式輸出的結果
公佈答案
t.age: 64, t.num: 156
可以看到對age 的賦值影響了num。因為他們在內的排布是連續的,如果將age 轉化為int64,那麼它會擴充套件到num的記憶體空間,當賦值age 的時候自然會影響到num了。
所以這就是為啥這個包的名字叫做unsafe,它代表不安全的記憶體操作了,需要開發者自己去保證。
最新評論