基本結構和基本數據類型 程序員
指針算法
不像 Java 和 .NET,Go 語言爲程序員提供了控制數據結構的指針的能力;可是,你不能進行指針運算。
經過給予程序員基本內存佈局,Go 語言容許你控制特定集合的數據結構、分配的數量以及內存訪問模
式,這些對構建運行良好的系統是很是重要的:指針對於性能的影響是不言而喻的,而若是你想要作的是
系統編程、操做系統或者網絡應用,指針更是不可或缺的一部分。編程
因爲各類緣由,指針對於使用面向對象編程的現代程序員來講可能顯得有些陌生,不過咱們將會在這一小
節對此進行解釋,並在將來的章節中展開深刻討論。數組
程序在內存中存儲它的值,每一個內存塊(或字)有一個地址,一般用十六進制數表示,如: 0x6b0820 或
0xf84001d7f0 。安全
Go 語言的取地址符是 & ,放到一個變量前使用就會返回相應變量的內存地址。
下面的代碼片斷(示例 4.9 pointer.go)可能輸出 An integer: 5, its location in memory: 0x6b0820
(這個值隨着你每次運行程序而變化)。網絡
var i1 = 5 fmt.Printf("An integer: %d, it's location in memory: %p\n", i1, &i1)
這個地址能夠存儲在一個叫作指針的特殊數據類型中,在本例中這是一個指向 int 的指針,即 i1 :此處
使用 *int 表示。若是咱們想調用指針 intP,咱們能夠這樣聲明它:數據結構
var intP *intide
而後使用 intP = &i1 是合法的,此時 intP 指向 i1。(指針的格式化標識符爲 %p )intP 存儲了 i1 的內存地址;函數
它指向了 i1 的位置,它引用了變量 i1。一個指針變量能夠指向任何一個值的內存地址 它指向那個值的內存地址,佈局
在 32 位機器上佔用 4 個字節,在 64 位機器上佔用 8 個字節,而且與它所指向的值的大小無關。固然,
能夠聲明指針指向任何類型的值來代表它的原始性或結構性;你能夠在指針類型前面加上 * 號(前綴)
來獲取指針所指向的內容,這裏的* 號是一個類型更改器。使用一個指針引用一個值被稱爲間接引用。
當一個指針被定義後沒有分配到任何變量時,它的值爲 nil 。一個指針變量一般縮寫爲 ptr 。
注意事項 在書寫表達式相似 var p *type 時,切記在 * 號和指針名稱間留有一個空格,由於 - var p*type 是 語法正確的,可是在更復雜的表達式中,它容易被誤認爲是一個乘法表達式!
符號 * 能夠放在一個指針前,如 *intP ,那麼它將獲得這個指針指向地址上所存儲的值;這被稱爲反引用
(或者內容或者間接引用)操做符;另外一種說法是指針轉移。
對於任何一個變量 var, 以下表達式都是正確的: var == *(&var) 。
如今,咱們應當能理解 pointer.go 中的整個程序和他的輸出:
示例 pointer.go:
package main import "fmt" func main() { var i1 = 5 fmt.Printf("An integer: %d, its location in memory: %p\n", i1, &i1) var intP *int intP = &i1 fmt.Printf("The value at memory location %p is %d\n", intP, *intP) }
輸出:
程序 string_pointer.go 爲咱們展現了指針對string的例子。
它展現了分配一個新的值給 *p 而且更改這個變量本身的值(這裏是一個字符串)。
示例 string_pointer.go
package main import "fmt" func main() { s := "good bye" var p *string = &s *p = "ciao" fmt.Printf("Here is the pointer p: %p\n", p) // prints address fmt.Printf("Here is the string *p: %s\n", *p) // prints string fmt.Printf("Here is the string s: %s\n", s) // prints same string }
輸出:
經過對 *p 賦另外一個值來更改「對象」,這樣 s 也會隨之更改 。
注意事項
你不能獲得一個文字或常量的地址,例如
注意事項
你不能獲得一個文字或常量的地址,例如 :
const i = 5 ptr := &i //error: cannot take the address of i ptr2 := &10 //error: cannot take the address of 10
因此說,Go 語言和 C、C++ 以及 D 語言這些低級(系統)語言同樣,都有指針的概念。可是對於常常導
致 C 語言內存泄漏繼而程序崩潰的指針運算(所謂的指針算法,如: pointer+2 ,移動指針指向字符串
的字節數或數組的某個位置)是不被容許的。Go 語言中的指針保證了內存安全,更像是 Java、C# 和VB.NET 中的引用。
所以 c = *p++ 在 Go 語言的代碼中是不合法的。
指針的一個高級應用是你能夠傳遞一個變量的引用(如函數的參數),這樣不會傳遞變量的拷貝。指針傳
遞是很廉價的,只佔用 4 個或 8 個字節。當程序在工做中須要佔用大量的內存,或不少變量,或者二者都
有,使用指針會減小內存佔用和提升效率。被指向的變量也保存在內存中,直到沒有任何指針指向它們,
因此從它們被建立開始就具備相互獨立的生命週期。
另外一方面(雖然不太可能),因爲一個指針致使的間接引用(一個進程執行了另外一個地址),指針的過分
頻繁使用也會致使性能降低。
指針也能夠指向另外一個指針,而且能夠進行任意深度的嵌套,致使你能夠有多級的間接引用,但在大多數
狀況這會使你的代碼結構不清晰。
如咱們所見,在大多數狀況下 Go 語言可使程序員輕鬆建立指針,而且隱藏間接引用,如:自動反向引用。
對一個空指針的反向引用是不合法的,而且會使程序崩潰:
示例 testcrash.go:
package main func main() { var p *int = nil *p = 0 }// in Windows: stops only with: <exit code="-1073741819" msg="process crashed"/> // runtime error: invalid memory address or nil pointer dereference