這裡主要分成四個部分。
了解指標
當我們把 int, string, bool 這類值傳給函式處理時,Go語言會在函式裡複製這些值,建立新的變數。
這個複製動作意味著你在呼叫函式時,若函式對參數做出更動,原始的值也不會受到影響,能減少程式碼的錯誤。
對於這種傳值方式,Go語言採用了一種簡單的記憶體管理系統 — 堆疊 (Stack)
每個參數都會在堆疊中獲得自己的記憶體。缺點是有越來越多的數值在函式裡傳遞的時候,這樣的複製動作就會消耗掉記憶體。
其實還有一種方法,就是建立 指標
,指標跟值是兩回事,而指標的唯一用途就是取得值而已。
對一個值建立指標後,Go 語言就無法以堆疊來管理該值所用的記憶體。這是 堆疊
必須仰賴的簡單變數範圍邏輯 ( scope loop )
,以便得知如何回收該值所使用的記憶體,以上規則不適用於指標。
Go 語言轉而會把值放在所謂的 堆積 ( heap ) 記憶體空間 : 堆積勇許一個值存在,直到程式沒有任何指標參照到它為止。
Go 語言會使用所謂的 垃圾回收機制 ( garbage collection ) 程序來回收這些記憶體。
CPU vs 指標
如果以指標取代值來傳遞給大量函式,對記憶體的好處不可言喻,可是對 CPU 的負擔就很難評估了~
取得指標
可以使用 var
以這種方式的初始值會是 nil
使用 new()
1 2
| <變數> := new(<type>) var <變數> = new(<type>)
|
要取得某個既有變數的指標,請利用&
算符
我們取一個例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import ( "fmt" "time" )
func main() { var count1 *int count2 := new(int)
countTemp := 5 count3 := &countTemp
t := &time.Time{}
fmt.Printf("count1 : %#v \n", count1) fmt.Printf("count2 : %#v \n", count2) fmt.Printf("count3 : %#v \n", count3) fmt.Printf("t : %#v \n", t) }
|
從指標取得值
如果要真正取得指標的關聯值 ( 即指標指向的記憶體所儲存的內容 ),必須把 * 算符放在變數名稱前面,以便解除 ( derefence ) 指標的參照、真正的取得值
接下來我們來實作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package main
import ( "fmt" "time" )
func main() { var count1 *int count2 := new(int)
countTemp := 5 count3 := &countTemp
t := &time.Time{}
if count1 != nil { fmt.Printf("count1 : %#v \n", *count1) } if count2 != nil { fmt.Printf("count2 : %#v \n", *count2) } if count3 != nil { fmt.Printf("count3 : %#v \n", *count3) } if t != nil { fmt.Printf("t : %#v \n", t.String()) } }
|
使用指標的函式設計
這邊就直接實作 * 的部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package main
import ( "fmt" )
func add1(count int) { count = count + 5 fmt.Println("add1 value: ", count) }
func add2(count *int) { *count = *count + 5 fmt.Println("add2 point: ", *count) }
func main() { var count int add1(count) fmt.Println("add1 post : ", count) add2(&count) fmt.Println("add2 post : ", count) }
|