亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Golang基礎(chǔ)常識性面試中常見的六大陷阱及應(yīng)對技巧總結(jié)

 更新時(shí)間:2024年08月06日 09:53:53   作者:少林碼僧  
Go是一門簡單有趣的語言,但與其他語言類似,它會(huì)有一些技巧,這篇文章主要給大家介紹了關(guān)于Golang基礎(chǔ)常識性面試中常見的六大陷阱及應(yīng)對技巧的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

一、nil slice & empty slice

1、nil切片與空切片底層

  • nil切片:var nilSlice [] string

    • nil slice的長度len和容量cap都是0

    • nil slice==nil

    • nil slice的pointer是nil

  • 空切片:emptySlice0 := make([]int,0)

    • empty slice的長度是0,容量是由指向底層數(shù)組決定

    • empty slice != nil

    • empty slice的pointer是底層數(shù)組的地址

  • nil切片和空切片最大的區(qū)別在指向的數(shù)組引用地址是不一樣的

  • nil空切片引用數(shù)組指針地址為0(無指向任何實(shí)際地址)

圖片

  • 空切片的引用數(shù)組指針地址是有的,且固定為一個(gè)值,所有的空切片指向的數(shù)組引用地址都是一樣的

圖片

2、創(chuàng)建nil slice 和empty slice

package main
import "fmt"
func main() {
  var nilSlice []string  // 創(chuàng)建一個(gè) nil 切片
  emptySlice0 := make([]int, 0)  // 方法1:創(chuàng)建一個(gè)空切片(零切片)
  var emptySlice1 = []string{}   // 方法2:創(chuàng)建一個(gè)空切片
  fmt.Printf("\nnilSlice---> Nil:%v Len:%d Capacity:%d", nilSlice == nil, len(nilSlice), cap(nilSlice))
  fmt.Printf("\nemptySlice0---> nil:%v Len:%d Capacity:%d", emptySlice0 == nil, len(emptySlice0), cap(emptySlice0))
  fmt.Printf("\nemptySlice1---> nil:%v Len:%d Capacity:%d", emptySlice1 == nil, len(emptySlice1), cap(emptySlice1))
  // nil切片和空切片都可以正常 append數(shù)據(jù)
  nilSlice = append(nilSlice, "sss")
}
/*
Nil:true Len:0 Capacity:0
nil:false Len:0 Capacity:0
nil:false Len:0 Capacity:0[sss]
 */

二、類型強(qiáng)轉(zhuǎn)產(chǎn)生內(nèi)存拷貝

1、字符串轉(zhuǎn)數(shù)組發(fā)送內(nèi)存拷貝

  • 字符串轉(zhuǎn)成byte數(shù)組,會(huì)發(fā)生內(nèi)存拷貝嗎?

  • 字符串轉(zhuǎn)出切片,會(huì)產(chǎn)生拷貝

  • 嚴(yán)格來說,只要是發(fā)送類型強(qiáng)轉(zhuǎn)都會(huì)發(fā)送內(nèi)存拷貝

  • 那么問題來了,頻繁的內(nèi)存拷貝操作聽起來對性能不大友好

  • 有沒有什么辦法可以在字符串轉(zhuǎn)出切片的時(shí)候不用發(fā)生拷貝呢?

2、字符串轉(zhuǎn)數(shù)組不內(nèi)存拷貝方法

  • 那么如果想要在底層轉(zhuǎn)換二者,只需要吧StringHeader的地址強(qiáng)轉(zhuǎn)成SliceHeader就行,那么go有個(gè)很強(qiáng)的包叫unsafe

  • 1.unsafe.Pointer(&a)方法可以得到變量a的地址。

    • 2.(*reflect.StringHeader)(unsafe.Pointer(&a)) 可以把字符串a(chǎn)轉(zhuǎn)成底層結(jié)構(gòu)的形式。

    • 3.(*[]byte)(unsafe.Pointer(&ssh)) 可以把ssh底層結(jié)構(gòu)體轉(zhuǎn)成byte的切片的指針。

    • 4.再通過 *轉(zhuǎn)為指針指向的實(shí)際內(nèi)容。

package main

import (
   "fmt"
   "reflect"
   "unsafe"
)

func main() {
   a :="aaa"
   ssh := *(*reflect.StringHeader)(unsafe.Pointer(&a))
   b := *(*[]byte)(unsafe.Pointer(&ssh))
   fmt.Printf("%v---%T",b,b)  // [97 97 97]---[]uint8
}

三、拷貝大切片一定代價(jià)大嗎?

  • SliceHeader 是切片在go的底層結(jié)構(gòu)。

    • 第一個(gè)字是指向切片底層數(shù)組的指針,這是切片的存儲空間

    • 第二個(gè)字段是切片的長度

    • 第三個(gè)字段是容量

type SliceHeader struct {
  Data uintptr
  Len  int
  Cap  int
}
  • 大切片跟小切片的區(qū)別無非就是 Len 和 Cap的值比小切片的這兩個(gè)值大一些,如果發(fā)生拷貝,本質(zhì)上就是拷貝上面的三個(gè)字段。

  • 所以 拷貝大切片跟小切片的代價(jià)應(yīng)該是一樣的

四、map不初始化使用會(huì)怎么樣

  • 空map和nil map結(jié)果是一樣的,都為map[]。

  • 所以,這個(gè)時(shí)候別斷定map是空還是nil,而應(yīng)該通過map == nil來判斷。

package main

func main() {
  var m1 map[string]string    // 創(chuàng)建一個(gè) nil map
  println("m1為nil: ", m1==nil)
  // 報(bào)錯(cuò) => panic: assignment to entry in nil map
  //m1["name"] = "tom"

  var m2 =  make(map[string]string)  // 創(chuàng)建一個(gè)空map
  m2["name"] = "jack"                // 空map可以正常
  println("m2為nil: ", m2==nil)
}

五、map會(huì)遍歷刪除安全嗎?

  • map 并不是一個(gè)線程安全的數(shù)據(jù)結(jié)構(gòu)。

  • 同時(shí)讀寫一個(gè) map 是未定義的行為,如果被檢測到,會(huì)直接 panic。

  • 上面說的是發(fā)生在多個(gè)協(xié)程同時(shí)讀寫同一個(gè) map 的情況下。

  • 如果在同一個(gè)協(xié)程內(nèi)邊遍歷邊刪除,并不會(huì)檢測到同時(shí)讀寫,理論上是可以這樣做的。

  • sync.Map可以解決多線程讀寫map問題

    • 一般而言,這可以通過讀寫鎖來解決:sync.RWMutex。

    • 讀之前調(diào)用 RLock() 函數(shù),讀完之后調(diào)用 RUnlock() 函數(shù)解鎖;

    • 寫之前調(diào)用 Lock() 函數(shù),寫完之后,調(diào)用 Unlock() 解鎖。

    • 另外,sync.Map 是線程安全的 map,也可以使用

六、for循環(huán)append坑

1、坑1:添加元素變覆蓋

  • 不會(huì)死循環(huán),for range其實(shí)是golang語法糖,在循環(huán)開始前會(huì)獲取切片的長度 len(切片),然后再執(zhí)行len(切片)次數(shù)的循環(huán)。

package main
import "fmt"
func main() {
  s := []int{1,2,3,4,5}
  for _, v:=range s {
    s =append(s, v)
    fmt.Printf("len(s)=%v\n",len(s))
  }
}
/*
len(s)=6
len(s)=7
len(s)=8
len(s)=9
len(s)=10
 */

2、坑2:值全部一樣

  • 每次循轉(zhuǎn)中num的值是正常的,但是由append構(gòu)造的res中,全是nums的最后一個(gè)值。

  • 最終總結(jié)出原因是在for range語句中,創(chuàng)建了變量num且只被創(chuàng)建了一次。

  • 即num有自己的空間內(nèi)存且地址在for循環(huán)過程中不變

  • 循環(huán)過程中每次將nums中對應(yīng)的值和num進(jìn)行值傳遞

package main
import "fmt"
func main() {
  var nums = []int{1, 2, 3, 4, 5}
  var res []*int
  for _, num := range nums {
    res = append(res, &num)
    //fmt.Println("num:", num)
  }
  for _, r := range res {
    fmt.Println("res:", *r)
  }
}
/*
res: 5
res: 5
res: 5
res: 5
res: 5
 */

3、解決方法

  • 方法1

    • 不使用for range的形式,直接用索引來對nums取值

package main
import "fmt"
func main() {
  var nums = []int{1, 2, 3, 4, 5}
  var res []*int
  for i := 0; i < len(nums); i++ {
    res = append(res, &nums[i])
  }
  fmt.Println("res:", res)
  for _, r := range res {
    fmt.Println("res:", *r)
  }
}
  • 方法2

    • 在for循環(huán)中每次再定義一個(gè)新的變量num_temp,將num的值傳給num_temp,之后append該變量即可。

package main
import "fmt"
func main() {
  var nums = []int{1, 2, 3, 4, 5}
  var res []*int
  for _, num := range nums {
    numTemp := num // 創(chuàng)建一個(gè)新的臨時(shí)變量
    res = append(res, &numTemp)
  }
  for _, r := range res {
    fmt.Println("res:", *r)
  }
}
/*
res: 1
res: 2
res: 3
res: 4
res: 5
 */

總結(jié)

到此這篇關(guān)于Golang基礎(chǔ)常識性面試中常見的六大陷阱及應(yīng)對技巧總結(jié)的文章就介紹到這了,更多相關(guān)Golang面試常見陷阱及應(yīng)對內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang使用sync.singleflight解決熱點(diǎn)緩存穿透問題

    golang使用sync.singleflight解決熱點(diǎn)緩存穿透問題

    在go的sync包中,有一個(gè)singleflight包,里面有一個(gè)?singleflight.go文件,代碼加注釋,一共200行出頭,通過?singleflight可以很容易實(shí)現(xiàn)緩存和去重的效果,避免重復(fù)計(jì)算,接下來我們就給大家詳細(xì)介紹一下sync.singleflight如何解決熱點(diǎn)緩存穿透問題
    2023-07-07
  • golang讀取yaml配置文件的方法實(shí)現(xiàn)

    golang讀取yaml配置文件的方法實(shí)現(xiàn)

    本文主要介紹了golang讀取yaml配置文件的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-10-10
  • Golang中Set類型的實(shí)現(xiàn)方法示例詳解

    Golang中Set類型的實(shí)現(xiàn)方法示例詳解

    這篇文章主要給大家介紹了關(guān)于Golang中Set類型實(shí)現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-09-09
  • golang雙鏈表的實(shí)現(xiàn)代碼示例

    golang雙鏈表的實(shí)現(xiàn)代碼示例

    這篇文章主要介紹了golang雙鏈表的實(shí)現(xiàn)代碼示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • go語言的四數(shù)相加等于指定數(shù)算法

    go語言的四數(shù)相加等于指定數(shù)算法

    這篇文章主要介紹了go語言的四數(shù)相加等于指定數(shù)算法的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 淺析Go使用定時(shí)器時(shí)如何避免潛在的內(nèi)存泄漏陷阱

    淺析Go使用定時(shí)器時(shí)如何避免潛在的內(nèi)存泄漏陷阱

    這篇文章來和大家一起探討一下Go?中如何高效使用?timer,特別是與select?一起使用時(shí),如何防止?jié)撛诘膬?nèi)存泄漏問題,感興趣的可以了解下
    2024-01-01
  • Go REFLECT Library反射類型詳解

    Go REFLECT Library反射類型詳解

    這篇文章主要為大家介紹了Go REFLECT Library反射類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go語言如何使用golang-jwt/jwt/v4進(jìn)行JWT鑒權(quán)詳解

    Go語言如何使用golang-jwt/jwt/v4進(jìn)行JWT鑒權(quán)詳解

    最近項(xiàng)目中需要用到鑒權(quán)機(jī)制,golang中jwt可以用,這篇文章主要給大家介紹了關(guān)于Go語言如何使用golang-jwt/jwt/v4進(jìn)行JWT鑒權(quán)的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • 淺談Golang如何使用Viper進(jìn)行配置管理

    淺談Golang如何使用Viper進(jìn)行配置管理

    在Golang生態(tài)中,Viper是一個(gè)不錯(cuò)的開源配置管理框架,這篇文章主要為大家介紹了Golang如何使用Viper進(jìn)行配置管理,需要的可以參考一下
    2023-06-06
  • Go經(jīng)典面試題匯總(填空+判斷)

    Go經(jīng)典面試題匯總(填空+判斷)

    這篇文章主要介紹了Go經(jīng)典面試題匯總(填空+判斷),本文章內(nèi)容詳細(xì),具有很好的參考價(jià)值,希望對大家有所幫助,需要的朋友可以參考下
    2023-01-01

最新評論