對於過去只有腳本語言(Scripting language)的經驗的人來說(例如說我),可能會對Pointer這個概念相當陌生,但這普遍出現在編譯語言(Compiled language)當中。有人說golang就像是進化版的C語言,再加上一點腳本語言的影子。儘管golang也支援指標,但相較於在C裡面的使用情況相對單純許多,不用進行指標運算,也不需要手動進行記憶體釋放,相對親切許多。
指標本身的概念並不複雜:將變數指向記憶體位置(memory address)就叫做pointer
,要修改內容時直接在記憶體位置中修改。我們通常不會直接使用指標的值,而是通過指標間接操作另外一個值。
我們來看個簡單的例子:
package main
import "fmt"
func main() {
var i *int // 宣告i是一個int的指標,目前還不知道會指向哪邊
a := 10 // a佔用了一個記憶體空間
i = &a // 將i指到a的記憶體位置
fmt.Println(i) // i所指到的記憶體位置
fmt.Println(*i) // *代表顯示該記憶體位置的值
}
執行後我們可以得到i的記憶體位置與值,分別是:
0xc000012098
10
我們也可以動態配置記憶體,比方說:
func main() {
// 宣告一個空的int
n := new(int)
// 直接把值指向記憶體位置
*n = 2
fmt.Println(n)
fmt.Println(*n)
}
記憶體位置會在給值之後產生,執行後一樣會得到:
0xc0000120b8
2
當然記憶體位置是不同的。
有了這個觀念以後,我們就能比較傳值與傳指標的不同,延續剛剛的範例,我們建立兩個函數分別接受值與指標作為參數:
func main() {
n := new(int)
*n = 2
fmt.Println(n)
foo_value(*n)
foo_point(n)
}
func foo_value(x int) {
fmt.Println(&x) // function內x的記憶體位置
}
func foo_point(x *int) {
fmt.Println(x) // function內x的記憶體位置
}
會分別得到:
0xc000098020
0xc000098030
0xc000098020
第一個與第三個記憶體位置相同,因為是同一個指標;作為值傳入到foo_value
,內部就會產生新的記憶體位置,與原本的main裡面的空間是不會互相影響的。
既然在有些狀況下傳值與傳指標都能做到類似的事情,那我們怎麼知道函數在什麼時候要使用哪一種作為變數呢?根據這一點,go官方在文件上已經有說明了(看來非常多人有相同的疑問),簡單整理一下:
感覺官方相當鼓勵大家多使用指標而不是值的方式呢,希望今天的內容對各位有幫助。