Go 語言的作者之一 Ken Thompson 也是 C 語言的作者。所以,Go 可以看作 C 系語言,它的很多特性都和 C 類似,指針就是其中之一。
然而,Go 語言的指針相比 C 的指針有很多限制。這當然是爲了安全考慮,要知道像 Java/Python 這些現代語言,生怕程序員出錯,哪有什麼指針(這裏指的是顯式的指針)?更別說像 C/C++ 還需要程序員自己清理“垃圾”。所以對於 Go 來說,有指針已經很不錯了,僅管它有很多限制。
相比於 C 語言中指針的靈活,Go 的指針多了一些限制。但這也算是 Go 的成功之處:既可以享受指針帶來的便利,又避免了指針的危險性。
限制一:Go 的指針不能進行數學運算
。
來看一個簡單的例子:
a := 5
p := &a
p++
p = &a + 3
上面的代碼將不能通過編譯,會報編譯錯誤:invalid operation
,也就是說不能對指針做數學運算。
限制二:不同類型的指針不能相互轉換
。
例如下面這個簡短的例子:
func main() {
a := int(100)
var f *float64
f = &a
}
也會報編譯錯誤:
cannot use &a (type *int) as type *float64 in assignment
限制三:不同類型的指針不能使用 == 或 != 比較
。
只有在兩個指針類型相同或者可以相互轉換的情況下,纔可以對兩者進行比較。另外,指針可以通過 ==
和 !=
直接和 nil
作比較。
限制四:不同類型的指針變量不能相互賦值
。
這一點同限制三。
unsafe.Pointer 在 unsafe 包:
type ArbitraryType int
type Pointer *ArbitraryType
從命名來看,Arbitrary
是任意的意思,也就是說 Pointer 可以指向任意類型,實際上它類似於 C 語言裏的 void*
。
unsafe 包提供了 2 點重要的能力:
- 任何類型的指針和 unsafe.Pointer 可以相互轉換。
- uintptr 類型和 unsafe.Pointer 可以相互轉換。
pointer 不能直接進行數學運算,但可以把它轉換成 uintptr,對 uintptr 類型進行數學運算,再轉換成 pointer 類型。
// uintptr 是一個整數類型,它足夠大,可以存儲
type uintptr uintptr
還有一點要注意的是,uintptr 並沒有指針的語義,意思就是 uintptr 所指向的對象會被 gc 無情地回收。而 unsafe.Pointer 有指針語義,可以保護它所指向的對象在“有用”的時候不會被垃圾回收。
unsafe 包中的幾個函數都是在編譯期間執行完畢,畢竟,編譯器對內存分配這些操作“瞭然於胸”。在 /usr/local/go/src/cmd/compile/internal/gc/unsafe.go
路徑下,可以看到編譯期間 Go 對 unsafe 包中函數的處理。