標籤:
幾乎可以肯定的說,go語言中除了閉包在引用外部變數的時候是傳引用的,其他的時候都是傳值的。如果你說形參可以定義為指標。好吧,那麼告訴你這個指標的值其實是按照傳值的方式使用的。
下面看個很淺顯的例子:
func stillTest(v int) { v = v + 100}
i := 100fmt.Println("i ", i)stillTest(i)fmt.Println("after i ", i)
輸出:
i 100after i 100
兩個值是不會有什麼區別的。但是指標就會有什麼區別嗎?
func anotherStillTest(v *int) { *v = *v + 100}
fmt.Println("i ", i)anotherStillTest(&i)fmt.Println("after i ", i)
輸出:
i 100after i 200
你看到i的值改了,你大喊這難道不是傳的引用嗎。man,仔細看看下面的例子。
func addressStillTest(v *int) { x := 456 v = &x}
x := 1000fmt.Println("x ", x)addressStillTest(&x)fmt.Println("after x ", x)
輸出:
x 1000after x 1000
是的,第一個方法中傳了一個地址進去,但是我們明顯不是對地址做的任何修改操作,而是做了一個dereference操作。然後修改了變數的值。而在上面的這個例子中才是對地址的操作。我們在函數addressStillTest中試圖修改x指向的地址,由於x的地址是傳值操作的,也就是拷貝過來的,所以修改是無效的。最後的輸出結果也說明了這一點。
所以在函數操作方面,任何的參數都是按照傳值操作的方式執行的。不管是穿的指標還是一般的一個值都是傳值使用的。
下面再看看這個結構體的例子。首先需要有這個:
type Dog struct { Name string Type string}
func addressTest(d *Dog) { a := &Dog{"another cute dog", "another type"} d = a }
輸出:
Dog 5 6Another Dog 5 6
對結構體直接做更換地址的操作還是不起作用。再一次表面函數的指標也是傳值操作的。
如果要修改一個結構體呢?
func anotherTest(d *Dog) { a := &Dog{"another cute dog", "another type"} d.Name = a.Name d.Type = a.Type}
輸出:
Dog cute dog ...Another Dog another cute dog another type
最後說明一個問題。在c,c++裡如果從函數內部返回一個局部變數的指標的話是不對的。但是在Go裡是可以的。Go的編譯器會檢查函數的局部變數指標是否會作為傳回值給外部使用,如果是的話則將這個變數放在heap上延長其生命週期。
func test() *Dog { return &Dog{"cute dog", "..."}}
d := test()fmt.Println("Dog ", d.Name, d.Type)
輸出:
Dog cute dog ...
坑已填平!
go指標的一個小坑