4.4.2 值類型和引用類型
所有像 int、float、bool 和 string 這些基本類型都屬於值類型,使用這些類型的變量直接指向存在內存中的值。
Go 語言的取地址符是 &
,放到一個變量前使用就會返回相應變量的內存地址。
示例 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 也會隨之更改。
內存示意圖如下:
傳遞指針給函數不但可以節省內存(因為沒有復制變量的值),而且賦予了函數直接修改外部變量的能力,所以被修改的變量不再需要使用 return
返回。如下的例子,reply
是一個指向 int
變量的指針,通過這個指針,我們在函數內修改了這個 int
變量的數值。
示例 6.6 side_effect.go
package main
import ( "fmt" ) // this function changes reply: func Multiply(a, b int, reply *int) { *reply = a * b } func main() { n := 0 reply := &n Multiply(10, 5, reply) fmt.Println("Multiply:", *reply) // Multiply: 50 }
在 Go 語言中,指針(第 4.9 節)屬於引用類型,其它的引用類型還包括 slices(第 7 章),maps(第 8 章)和 channel(第 13 章)。被引用的變量會存儲在堆中,以便進行垃圾回收,且比棧擁有更大的內存空間。
在函數調用時,像切片(slice)、字典(map)、接口(interface)、通道(channel)這樣的引用類型都是默認使用引用傳遞(即使沒有顯式的指出指針)。
4.4.5 init 函數
變量除了可以在全局聲明中初始化,也可以在 init 函數中初始化。這是一類非常特殊的函數,它不能夠被人為調用,而是在每個包完成初始化后自動執行,並且執行優先級比 main 函數高。
每一個源文件都可以包含一個或多個 init 函數。初始化總是以單線程執行,並且按照包的依賴關系順序執行。
一個可能的用途是在開始執行程序之前對數據進行檢驗或修復,以保證程序狀態的正確性。
示例 4.6 init.go:
package trans
import "math" var Pi float64 func init() { Pi = 4 * math.Atan(1) // init() function computes Pi }
在它的 init 函數中計算變量 Pi 的初始值。
示例 4.7 user_init.go 中導入了包 trans(在相同的路徑中)並且使用到了變量 Pi:
package main
import ( "fmt" "./trans" ) var twoPi = 2 * trans.Pi func main() { fmt.Printf("2*Pi = %g\n", twoPi) // 2*Pi = 6.283185307179586 }
init 函數也經常被用在當一個程序開始之前調用后台執行的 goroutine,如下面這個例子當中的 backend()
:
func init() { // setup preparations go backend() }