Go語言學習查缺補漏ing Day8

恆生LIGHT雲社群發表於2021-12-13

作者:ReganYue

來源: 恆生LIGHT雲社群

Go語言學習查缺補漏ing Day8

零、前言

因為筆者基礎不牢,在使用Go語言的時候經常遇到很多摸不著頭腦的問題,所以筆者下定決心好好對Go語言進行查漏補缺,本【Go語言查缺補漏ing】系列主要是幫助新手Gopher更好的瞭解Go語言的易錯點、重難點。希望各位看官能夠喜歡,點點贊、關注一下唄!

一、為什麼map的value值是不可定址的?解決辦法?

先來看下面這段程式碼:

package main


import "fmt"

type Hello struct {
  x, y int
}

var m = map[string]Hello{
  "hello": Hello{2, 3},
}

func main() {
  m["hello"].x = 4
  fmt.Println(m["hello"].x)
}

執行上面這個程式會報錯:

# command-line-arguments

.\demo.go:14:15: cannot assign to struct field m["hello"].x in map

為什麼呢?下面來進行詳細說明:

因為map是無法進行定址的,也就是說可以獲取m["hello"].x的值,但是不能對其值進行修改。

究其原因,因為Go的map是通過雜湊表來實現的,說得更具體一點,就是通過陣列和連結串列組合實現的。

並且Go的map也可以做到動態擴容,當進行擴容之後,map的value那塊空間地址就會產生變化,所以無法對map的value進行定址。

但是注意,map與slice切片的擴容有些不同,map是引用型別,擴容後,value引用地址不會變化,所以map value元素不可定址。而slice擴容後是生成一個新的底層陣列。

有什麼解決辦法呢?

解決辦法一:使用臨時變數

package main


import "fmt"

type Hello struct {
  x, y int
}

var m = map[string]Hello{
  "hello": Hello{2, 3},
}

func main() {
  tmp := m["hello"]
  tmp.x = 4
  m["hello"] = tmp
  fmt.Println(m["hello"].x)
}

解決辦法二:使用指標

package main


import "fmt"

type Hello struct {
  x, y int
}

var m = map[string]*Hello{
  "hello": &Hello{2, 3},
}

func main() {
  m["hello"].x = 4
  fmt.Println(m["hello"].x)
}

二、遍歷切片的迴圈次數會不會改變

看下面這段程式碼會不會出現死迴圈:

package main


func main() {
  values := []int{1, 2, 3}
  for value := range values {
      values = append(values, value)
  }
}

答案是不會出現死迴圈,程式能夠正常退出。

這是因為迴圈次數在for...range 之前就已經確定了,迴圈之內改變切片的長度,並不會影響迴圈次數。

三、 for...range複用臨時變數

看一看下面這段程式碼,你認為會輸出什麼?

package main


import (
  "fmt"
  "time"
)

func main() {
  var s = [...]int{1, 2, 3}
  for index, value := range s {
      go func() {
          fmt.Println(index, value)
      }()
  }
  time.Sleep(time.Second * 3)
}

image-20211126112113115

哈哈,有的小夥伴會不會很奇怪為什麼是輸出一樣的值?

因為這裡使用:=的形式迭代變數,index和value都會在每次迴圈被重用,並不會進行重新宣告。

所以各個協程都是輸出迴圈結束後的index以及value值,而不是每個協程開始時的index以及value值。

那有什麼解決辦法呢?

解決辦法之一:使用函式引數進行傳遞

package main


import (
  "fmt"
  "time"
)

func main() {
  var s = [...]int{1, 2, 3}
  for index, value := range s {
      go func(index,value int) {
          fmt.Println(index, value)
      }(index,value)
  }
  time.Sleep(time.Second * 3)
}

image-20211126113014044

這樣就能解決了。

解決辦法二:使用臨時變數進行傳遞

package main


import (
  "fmt"
  "time"
)

func main() {
  var s = [...]int{1, 2, 3}
  for index, value := range s {
      i := index
      v := value
      go func() {
          fmt.Println(i, v)
      }()
  }
  time.Sleep(time.Second * 3)
}

image-20211126113203098

這樣也能解決這個問題。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70001864/viewspace-2847487/,如需轉載,請註明出處,否則將追究法律責任。

相關文章