那么 Golang 為什么需要指針?這種指針又能有什么獨特的用途呢? (推薦學(xué)習:go)
在學(xué)習引用類型語言的時候,總是要先搞清楚,當給一個函數(shù)/方法傳參的時候,傳進去的是值還是引用。
實際上,在大部分引用型語言里,參數(shù)為基本類型時,傳進去的大都是值,也就是另外復(fù)制了一份參數(shù)到當前的函數(shù)調(diào)用棧。參數(shù)為高級類型時,傳進去的基本都是引用。這個主要是因為虛擬機的內(nèi)存管理導(dǎo)致的。
內(nèi)存管理中的內(nèi)存區(qū)域一般包括 heap 和 stack, stack 主要用來存儲當前調(diào)用棧用到的簡單類型數(shù)據(jù):string,boolean,int,float 等。
這些類型的內(nèi)存占用小,容易回收,基本上它們的值和指針占用的空間差不多,因此可以直接復(fù)制,GC也比較容易做針對性的優(yōu)化。
復(fù)雜的高級類型占用的內(nèi)存往往相對較大,存儲在 heap 中,GC 回收頻率相對較低,代價也較大,因此傳引用/指針可以避免進行成本較高的復(fù)制操作,并且節(jié)省內(nèi)存,提高程序運行效率。
因此,在下列情況可以考慮使用指針:1,需要改變參數(shù)的值;2,避免復(fù)制操作;3,節(jié)省內(nèi)存;
而在 Golang 中,具體到高級類型 struct,slice,map,也各有不同。實際上,只有 struct 的使用有點復(fù)雜,slice,map,chan 都可以直接使用,不用考慮是值還是指針。
Go 有指針,但是沒有指針運算。你不能用指針變量遍歷字符串的各個字節(jié)。在 Go 中調(diào)用函數(shù)的時候,得記得變量是值傳遞的。
通過類型作為前綴來定義一個指針’ * ’:var p * int。現(xiàn)在 p 是一個指向整數(shù)值的指針。所有新定義的變量都被賦值為其類型的零值,而指針也一樣。一個新定義的或者沒有任何指向的指針,有值 nil。
在其他語言中,這經(jīng)常被叫做空(NULL)指針,在 Go 中就是 nil 。讓指針指向某些內(nèi)容,可以使用取址操作符 ( & ),像這樣:
package main import "fmt" func main() { var p *int fmt.Printf("%vn",p) //← 打印 nil var i int //← 定義一個整形變量 i p = &i //← 使得 p 指向 i, 獲取 i 的地址 fmt.Printf("%vn",p) //打印內(nèi)存地址 *p = 6 fmt.Printf("%vn",*p) //打印6 fmt.Printf("%vn",i) //打印6 }
前面已經(jīng)說了,沒有指針運算,所以如果這樣寫: *p++ ,它表示 (*p)++ :首先獲取指針指向的值,然后對這個值加一。這里注意與C語言的區(qū)別。
對于Go語言,嚴格意義上來講,只有一種傳遞,也就是按值傳遞(by value)。當一個變量當作參數(shù)傳遞的時候,會創(chuàng)建一個變量的副本,然后傳遞給函數(shù)或者方法,你可以看到這個副本的地址和變量的地址是不一樣的。
當變量當做指針被傳遞的時候,一個新的指針被創(chuàng)建,它指向變量指向的同樣的內(nèi)存地址,所以你可以將這個指針看成原始變量指針的副本。
當這樣理解的時候,我們就可以理解成Go總是創(chuàng)建一個副本按值轉(zhuǎn)遞,只不過這個副本有時候是變量的副本,有時候是變量指針的副本。