1. 程式人生 > 其它 >記一次Golang結構體與指標的坑點

記一次Golang結構體與指標的坑點

  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: