Golang 中的指針 - Pointer


http://www.cnblogs.com/jasonxuli/p/6802289.html
 
Go 的原生數據類型可以分為基本類型和高級類型,基本類型主要包含 string, bool, int 及 float 系列,高級類型包含 struct,array/slice,map,chan, func 。
 
相比 Java,Python,Javascript 等引用類型的語言,Golang 擁有類似C語言的指針這個相對古老的特性。但不同於 C 語言,Golang 的指針是單獨的類型,而不是 C 語言中的 int 類型,而且也不能對指針做整數運算。從這一點看,Golang 的指針基本就是一種引用。
 
那么 Golang 為什么需要指針?這種指針又能有什么獨特的用途呢?
 
在學習引用類型語言的時候,總是要先搞清楚,當給一個函數/方法傳參的時候,傳進去的是值還是引用。實際上,在大部分引用型語言里,參數為基本類型時,傳進去的大都是值,也就是另外復制了一份參數到當前的函數調用棧。參數為高級類型時,傳進去的基本都是引用。這個主要是因為虛擬機的內存管理導致的。
 
內存管理中的內存區域一般包括 heap 和 stack, stack 主要用來存儲當前調用棧用到的簡單類型數據:string,boolean,int,float 等。這些類型的內存占用小,容易回收,基本上它們的值和指針占用的空間差不多,因此可以直接復制,GC也比較容易做針對性的優化。 復雜的高級類型占用的內存往往相對較大,存儲在 heap 中,GC 回收頻率相對較低,代價也較大,因此傳引用/指針可以避免進行成本較高的復制操作,並且節省內存,提高程序運行效率。
 
因此,在下列情況可以考慮使用指針:1,需要改變參數的值;2,避免復制操作;3,節省內存;
 
而在 Golang 中,具體到高級類型 struct,slice,map,也各有不同。實際上,只有 struct 的使用有點復雜,slice,map,chan 都可以直接使用,不用考慮是值還是指針。
 
 
struct:
 
對於函數(function),由函數的參數類型指定,傳入的參數的類型不對會報錯,例如:
 
func passValue(s struct){}
 
func passPointer(s *struct){}

 

對於方法(method),接收者(receiver)可以是指針,也可以是值,Golang 會在傳遞參數前自動適配以符合參數的類型。也就是:如果方法的參數是值,那么按照傳值的方式 ,方法內部對struct的改動無法作用在外部的變量上,例如:
 
package main
 
import "fmt"
 
type MyPoint struct {
    X int
    Y int
}
 
func printFuncValue(p MyPoint){
    p.X = 1
    p.Y = 1
    fmt.Printf(" -> %v", p)
}
 
func printFuncPointer(pp *MyPoint){
    pp.X = 1 // 實際上應該寫做 (*pp).X,Golang 給了語法糖,減少了麻煩,但是也導致了 * 的不一致
    pp.Y = 1
    fmt.Printf(" -> %v", pp)
}
 
func (p MyPoint) printMethodValue(){
    p.X += 1
    p.Y += 1
    fmt.Printf(" -> %v", p)
}
 
// 建議使用指針作為方法(method:printMethodPointer)的接收者(receiver:*MyPoint),一是可以修改接收者的值,二是可以避免大對象的復制
func (pp *MyPoint) printMethodPointer(){
    pp.X += 1
    pp.Y += 1
    fmt.Printf(" -> %v", pp)
}
 
func main(){
    p := MyPoint{0, 0}
    pp := &MyPoint{0, 0}
 
    fmt.Printf("\n value to func(value): %v", p)
    printFuncValue(p)
    fmt.Printf(" --> %v", p)
    // Output: value to func(value): {0 0} -> {1 1} --> {0 0}
 
    //printFuncValue(pp) // cannot use pp (type *MyPoint) as type MyPoint in argument to printFuncValue
 
    //printFuncPointer(p) // cannot use p (type MyPoint) as type *MyPoint in argument to printFuncPointer
 
    fmt.Printf("\n pointer to func(pointer): %v", pp)
    printFuncPointer(pp)
    fmt.Printf(" --> %v", pp)
    // Output: pointer to func(pointer): &{0 0} -> &{1 1} --> &{1 1}
 
    fmt.Printf("\n value to method(value): %v", p)
    p.printMethodValue()
    fmt.Printf(" --> %v", p)
    // Output: value to method(value): {0 0} -> {1 1} --> {0 0}
 
    fmt.Printf("\n value to method(pointer): %v", p)
    p.printMethodPointer()
    fmt.Printf(" --> %v", p)
    // Output: value to method(pointer): {0 0} -> &{1 1} --> {1 1}
 
    fmt.Printf("\n pointer to method(value): %v", pp)
    pp.printMethodValue()
    fmt.Printf(" --> %v", pp)
    // Output: pointer to method(value): &{1 1} -> {2 2} --> &{1 1}
 
    fmt.Printf("\n pointer to method(pointer): %v", pp)
    pp.printMethodPointer()
    fmt.Printf(" --> %v", pp)
    // Output: pointer to method(pointer): &{1 1} -> &{2 2} --> &{2 2}
}

 

slice :
 
slice 實際上相當於對其依附的 array 的引用,它不存儲數據,只是對 array 進行描述。因此,修改 slice 中的元素,改變會體現在 array 上,當然也會體現在該 array 的所有 slice 上。
可以使用 make([]int) 來創建並初始化 map 。
 
 
map :
 
使用 make(map[string]string) 返回的本身是個引用,可以直接用來操作:
 
map["name"]="Jason";

 

而如果使用 map 的指針,反而會產生錯誤:

*map["name"]="Jason"  //  invalid indirect of m["title"] (type string)
(*map)["name"]="Jason"  // invalid indirect of m (type map[string]string)

 

 
chan :
 
make(chan int) 返回的是可以直接使用的 channel 。
 
 
func :
 
在 Golang 中,func 可以作為一種值被返回,因此也可以使用類似 Python 的 decorator 的方式來加工函數。
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM