記一次Golang結構體與指標的坑點
阿新 • • 發佈:2022-03-10
Golang語法簡潔,但是也會因此帶來一些比較難以察覺的坑點
比如以下程式碼(可直接編譯執行):
package main import "fmt" type Test struct { test string temp string } func main() { a := []Test{ Test{"123", "456"}, Test{"1231", "4156"}, Test{"12312", "34156"}, } for k, v := range a { fmt.Println(k, v, &v.temp) } b := []*Test{ &Test{"123", "456"}, &Test{"1231", "4156"}, &Test{"12312", "34156"}, } for k, v := range b { fmt.Println(k, v, &v.temp) } }
在main函式當中,我們分別遍歷結構體陣列a,和結構體指標陣列b,打印出成員的值,以及地址,最終結果是這樣的:
0 {123 456} 0xc0000403d0 1 {1231 4156} 0xc0000403d0 2 {12312 34156} 0xc0000403d0 0 &{123 456} 0xc000040470 1 &{1231 4156} 0xc000040490 2 &{12312 34156} 0xc0000404b0
可以看到,前三次的地址一模一樣,後三次的地址卻不一樣了,這是為什麼呢?
留一部分空白,可以用來思考:)
這是因為Golang在range遍歷時,會根據遍歷的物件來建立一個對應的迭代器,然後把當前遍歷到的物件,值傳遞給這個迭代器。
在遍歷過程中,這個迭代器的地址是不會變的。
第一個數組裡,迭代器v是一個Test結構體;
因此&v.temp,每次取的都是迭代器的成員變數的地址,所以地址不會變。
而在第二個數組裡,迭代器v是一個Test結構體的指標;
雖然v的地址仍然不會改變,但是v當中存放的值,每次都會變,v中存放的,每次都是不同的結構體的地址。
因此&v.temp,每次取的是迭代器指向的物件內部的成員變數的地址,所以每次是不一樣的。
其實這裡有一點,那既然兩次取的物件不一樣,一個是迭代器本身,一個是迭代器指向的物件,那為什麼寫法都是&v.temp呢?
答案是因為Golang語法簡潔,點分符'.'的左邊,既可以是實體物件,也可以是指標物件,所以才造成了看上去的“疑惑點”
如果在c++裡面,就不能這麼表示了,會區分的更明顯一些,所以不會有這個坑。
實體引用使用'.',第一種取法是v.temp
指標引用使用'->',第二種取法是v->temp,
這樣就能明顯區別開兩種引用方法,所以這個問題,本質上是Golang語法上的小陷阱,畢竟語法簡潔,不是萬無一失的。:doge: