GO語言————4.9、指標
4.9 指標
不像 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 *int
然後使用 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 的全部內容及其輸出:
示例 4.21 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)
}
輸出:
An integer: 5, its location in memory: 0x24f0820
The value at memory location 0x24f0820 is 5
我們可以用下圖來表示記憶體使用的情況:
程式 string_pointer.go 為我們展示了指標對string的例子。
它展示了分配一個新的值給 *p 並且更改這個變數自己的值(這裡是一個字串)。
示例 4.22 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
}
輸出:
Here is the pointer p: 0x2540820
Here is the string *p: ciao
Here is the string s: ciao
通過對 *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 語言可以使程式設計師輕鬆建立指標,並且隱藏間接引用,如:自動反向引用。
對一個空指標的反向引用是不合法的,並且會使程式崩潰:
示例 4.23 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
問題 4.2 列舉 Go 語言中 * 號的所有用法。
相關文章
- go 語言指標學習Go指標
- Go 語言指標符號 *和&Go指標符號
- 徹底學會 Go 指標 -- 就要學習 Go 語言Go指標
- Go語言什麼時候該使用指標 與 指標使用分析Go指標
- C語言(指標)C語言指標
- C語言指標C語言指標
- C語言 C語言野指標C語言指標
- C語言指標(三):陣列指標和字串指標C語言指標陣列字串
- C語言-指標操作C語言指標
- Go語言高階資料型別之指標篇Go資料型別指標
- C語言指標(二) 指標變數 ----by xhxhC語言指標變數
- 《快學 Go 語言》第 14 課 —— 魔術變性指標Go指標
- C語言指標安全及指標使用問題C語言指標
- c語言指標彙總C語言指標
- C語言指標學習C語言指標
- c語言指標詳解C語言指標
- C語言基礎-指標C語言指標
- C語言 函式指標C語言函式指標
- C語言指標筆記C語言指標筆記
- go語言標準庫 - logGo
- go語言標準庫 - timeGo
- C語言知識彙總 | 51-C語言字串指標(指向字串的指標)C語言字串指標
- 搞清楚C語言指標C語言指標
- C語言 指標與陣列C語言指標陣列
- C語言指標基本知識C語言指標
- C語言指標詳解(一)C語言指標
- C語言指標詳解(二)C語言指標
- C語言指標用法大全C語言指標
- c語言實現this指標效果C語言指標
- C語言指標細節_1C語言指標
- 指標——C語言的靈魂指標C語言
- C語言基礎-1、指標C語言指標
- GO 指標Go指標
- C語言知識彙總 | 56-C語言NULL空指標以及void指標C語言Null指標
- Go語言入門系列(五)之指標和結構體的使用Go指標結構體
- go語言標準庫 - strconvGo
- GO語言————5.6 標籤與gotoGo
- C語言指標常見問題C語言指標