mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-10-24 07:21:41 +00:00
ch13: fix code path
This commit is contained in:
@@ -18,9 +18,8 @@ fmt.Printf("%#016x\n", Float64bits(1.0)) // "0x3ff0000000000000"
|
||||
|
||||
許多將unsafe.Pointer指針轉爲原生數字,然後再轉迴爲unsafe.Pointer類型指針的操作也是不安全的。比如下面的例子需要將變量x的地址加上b字段地址偏移量轉化爲`*int16`類型指針,然後通過該指針更新x.b:
|
||||
|
||||
<u><i>gopl.io/ch13/unsafeptr</i></u>
|
||||
```Go
|
||||
//gopl.io/ch13/unsafeptr
|
||||
|
||||
var x struct {
|
||||
a bool
|
||||
b int16
|
||||
@@ -37,10 +36,10 @@ fmt.Println(x.b) // "42"
|
||||
上面的寫法盡管很繁瑣,但在這里併不是一件壞事,因爲這些功能應該很謹慎地使用。不要試圖引入一個uintptr類型的臨時變量,因爲它可能會破壞代碼的安全性(譯註:這是眞正可以體會unsafe包爲何不安全的例子)。下面段代碼是錯誤的:
|
||||
|
||||
```Go
|
||||
// NOTE: subtly incorrect!
|
||||
tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
|
||||
pb := (*int16)(unsafe.Pointer(tmp))
|
||||
*pb = 42
|
||||
// NOTE: subtly incorrect!
|
||||
tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
|
||||
pb := (*int16)(unsafe.Pointer(tmp))
|
||||
*pb = 42
|
||||
```
|
||||
|
||||
産生錯誤的原因很微妙。有時候垃圾迴收器會移動一些變量以降低內存碎片等問題。這類垃圾迴收器被稱爲移動GC。當一個變量被移動,所有的保存改變量舊地址的指針必須同時被更新爲變量移動後的新地址。從垃圾收集器的視角來看,一個unsafe.Pointer是一個指向變量的指針,因此當變量被移動是對應的指針也必須被更新;但是uintptr類型的臨時變量隻是一個普通的數字,所以其值不應該被改變。上面錯誤的代碼因爲引入一個非指針的臨時變量tmp,導致垃圾收集器無法正確識别這個是一個指向變量x的指針。當第二個語句執行時,變量x可能已經被轉移,這時候臨時變量tmp也就不再是現在的`&x.b`地址。第三個向之前無效地址空間的賦值語句將徹底摧譭整個程序!
|
||||
@@ -66,5 +65,3 @@ func (Value) Pointer() uintptr
|
||||
func (Value) UnsafeAddr() uintptr
|
||||
func (Value) InterfaceData() [2]uintptr // (index 1)
|
||||
```
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user