iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
1
Software Development

啥物碗Golang? 30天就Go系列 第 24

Pointer 指標

對於過去只有腳本語言(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官方在文件上已經有說明了(看來非常多人有相同的疑問),簡單整理一下:

  • 對struct內的成員進行修改時,使用指標
  • 如果struct內有大量的成員,請使用指標節省記憶體空間
  • 為了一致性,如果團隊內有時使用指標有時使用值會造成混亂,建議一致使用指標

感覺官方相當鼓勵大家多使用指標而不是值的方式呢,希望今天的內容對各位有幫助。

Refernece


上一篇
Channels 通道
下一篇
Cookie 與 Session
系列文
啥物碗Golang? 30天就Go30

尚未有邦友留言

立即登入留言