最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言切片??嫉拿嬖囌骖}解析

 更新時間:2022年02月14日 15:03:11   作者:AsongGo  
了解最新的Go語言面試題型,讓面試不再是難事,下面這篇文章主要給大家介紹了關(guān)于Go語言切片面試常考的一些問題,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

哈嘍,大家好,我是asong。最近沒事在看八股文,總結(jié)了幾道??嫉那衅斯晌模詥柎鸬姆绞娇偨Y(jié)出來,希望對正在面試的你們有用~

本文題目不全,關(guān)于切片的面試真題還有哪些?歡迎評論區(qū)補充~

01. 數(shù)組和切片有什么區(qū)別?

Go語言中數(shù)組是固定長度的,不能動態(tài)擴(kuò)容,在編譯期就會確定大小,聲明方式如下:

var buffer [255]int
buffer := [255]int{0}

切片是對數(shù)組的抽象,因為數(shù)組的長度是不可變的,在某些場景下使用起來就不是很方便,所以Go語言提供了一種靈活,功能強悍的內(nèi)置類型切片("動態(tài)數(shù)組"),與數(shù)組相比切片的長度是不固定的,可以追加元素。切片是一種數(shù)據(jù)結(jié)構(gòu),切片不是數(shù)組,切片描述的是一塊數(shù)組,切片結(jié)構(gòu)如下:

我們可以直接聲明一個未指定大小的數(shù)組來定義切片,也可以使用make()函數(shù)來創(chuàng)建切片,聲明方式如下:

var slice []int // 直接聲明
slice := []int{1,2,3,4,5} // 字面量方式
slice := make([]int, 5, 10) // make創(chuàng)建
slice := array[1:5] // 截取下標(biāo)的方式
slice := *new([]int) // new一個

切片可以使用append追加元素,當(dāng)cap不足時進(jìn)行動態(tài)擴(kuò)容。

02. 拷貝大切片一定比拷貝小切片代價大嗎?

這道題比較有意思,原文地址:Are large slices more expensive than smaller ones?

這道題本質(zhì)是考察對切片本質(zhì)的理解,Go語言中只有值傳遞,所以我們以傳遞切片為例子:

func main()  {
 param1 := make([]int, 100)
 param2 := make([]int, 100000000)
 smallSlice(param1)
 largeSlice(param2)
}

func smallSlice(params []int)  {
 // ....
}

func largeSlice(params []int)  {
 // ....
}

切片param2要比param1大1000000個數(shù)量級,在進(jìn)行值拷貝的時候,是否需要更昂貴的操作呢?

實際上不會,因為切片本質(zhì)內(nèi)部結(jié)構(gòu)如下:

type SliceHeader struct {
 Data uintptr
 Len  int
 Cap  int
}

切片中的第一個字是指向切片底層數(shù)組的指針,這是切片的存儲空間,第二個字段是切片的長度,第三個字段是容量。將一個切片變量分配給另一個變量只會復(fù)制三個機(jī)器字,大切片跟小切片的區(qū)別無非就是Len 和 Cap的值比小切片的這兩個值大一些,如果發(fā)生拷貝,本質(zhì)上就是拷貝上面的三個字段。

03. 切片的深淺拷貝

深淺拷貝都是進(jìn)行復(fù)制,區(qū)別在于復(fù)制出來的新對象與原來的對象在它們發(fā)生改變時,是否會相互影響,本質(zhì)區(qū)別就是復(fù)制出來的對象與原對象是否會指向同一個地址。在Go語言,切片拷貝有三種方式:

使用=操作符拷貝切片,這種就是淺拷貝

使用[:]下標(biāo)的方式復(fù)制切片,這種也是淺拷貝

使用Go語言的內(nèi)置函數(shù)copy()進(jìn)行切片拷貝,這種就是深拷貝,

04. 零切片、空切片、nil切片是什么

為什么問題中這么多種切片呢?因為在Go語言中切片的創(chuàng)建方式有五種,不同方式創(chuàng)建出來的切片也不一樣;

  • 零切片

我們把切片內(nèi)部數(shù)組的元素都是零值或者底層數(shù)組的內(nèi)容就全是 nil的切片叫做零切片,使用make創(chuàng)建的、長度、容量都不為0的切片就是零值切片:

slice := make([]int,5) // 0 0 0 0 0
slice := make([]*int,5) // nil nil nil nil nil
  • nil切片

nil切片的長度和容量都為0,并且和nil比較的結(jié)果為true,采用直接創(chuàng)建切片的方式、new創(chuàng)建切片的方式都可以創(chuàng)建nil切片:

var slice []int
var slice = *new([]int)
  • 空切片

空切片的長度和容量也都為0,但是和nil的比較結(jié)果為false,因為所有的空切片的數(shù)據(jù)指針都指向同一個地址 0xc42003bda0;使用字面量、make可以創(chuàng)建空切片:

var slice = []int{}
var slice = make([]int, 0)

空切片指向的 zerobase 內(nèi)存地址是一個神奇的地址,從 Go 語言的源代碼中可以看到它的定義:

// base address for all 0-byte allocations
var zerobase uintptr

// 分配對象內(nèi)存
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
 ...
 if size == 0 {
  return unsafe.Pointer(&zerobase)
 }
  ...
}

05. 切片的擴(kuò)容策略

這個問題是一個高頻考點,我們通過源碼來解析一下切片的擴(kuò)容策略,切片的擴(kuò)容都是調(diào)用growslice方法,截取部分重要源代碼:

// runtime/slice.go
// et:表示slice的一個元素;old:表示舊的slice;cap:表示新切片需要的容量;
func growslice(et *_type, old slice, cap int) slice {
 if cap < old.cap {
  panic(errorString("growslice: cap out of range"))
 }

 if et.size == 0 {
  // append should not create a slice with nil pointer but non-zero len.
  // We assume that append doesn't need to preserve old.array in this case.
  return slice{unsafe.Pointer(&zerobase), old.len, cap}
 }

 newcap := old.cap
  // 兩倍擴(kuò)容
 doublecap := newcap + newcap
  // 新切片需要的容量大于兩倍擴(kuò)容的容量,則直接按照新切片需要的容量擴(kuò)容
 if cap > doublecap {
  newcap = cap
 } else {
    // 原 slice 容量小于 1024 的時候,新 slice 容量按2倍擴(kuò)容
  if old.cap < 1024 {
   newcap = doublecap
  } else { // 原 slice 容量超過 1024,新 slice 容量變成原來的1.25倍。
   // Check 0 < newcap to detect overflow
   // and prevent an infinite loop.
   for 0 < newcap && newcap < cap {
    newcap += newcap / 4
   }
   // Set newcap to the requested cap when
   // the newcap calculation overflowed.
   if newcap <= 0 {
    newcap = cap
   }
  }
 }

  // 后半部分還對 newcap 作了一個內(nèi)存對齊,這個和內(nèi)存分配策略相關(guān)。進(jìn)行內(nèi)存對齊之后,新 slice 的容量是要 大于等于 老 slice 容量的 2倍或者1.25倍。
 var overflow bool
 var lenmem, newlenmem, capmem uintptr
 // Specialize for common values of et.size.
 // For 1 we don't need any division/multiplication.
 // For sys.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
 // For powers of 2, use a variable shift.
 switch {
 case et.size == 1:
  lenmem = uintptr(old.len)
  newlenmem = uintptr(cap)
  capmem = roundupsize(uintptr(newcap))
  overflow = uintptr(newcap) > maxAlloc
  newcap = int(capmem)
 case et.size == sys.PtrSize:
  lenmem = uintptr(old.len) * sys.PtrSize
  newlenmem = uintptr(cap) * sys.PtrSize
  capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
  overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
  newcap = int(capmem / sys.PtrSize)
 case isPowerOfTwo(et.size):
  var shift uintptr
  if sys.PtrSize == 8 {
   // Mask shift for better code generation.
   shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
  } else {
   shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
  }
  lenmem = uintptr(old.len) << shift
  newlenmem = uintptr(cap) << shift
  capmem = roundupsize(uintptr(newcap) << shift)
  overflow = uintptr(newcap) > (maxAlloc >> shift)
  newcap = int(capmem >> shift)
 default:
  lenmem = uintptr(old.len) * et.size
  newlenmem = uintptr(cap) * et.size
  capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
  capmem = roundupsize(capmem)
  newcap = int(capmem / et.size)
 }
}

通過源代碼可以總結(jié)切片擴(kuò)容策略:

切片在擴(kuò)容時會進(jìn)行內(nèi)存對齊,這個和內(nèi)存分配策略相關(guān)。進(jìn)行內(nèi)存對齊之后,新 slice 的容量是要 大于等于老 slice 容量的 2倍或者1.25倍,當(dāng)原 slice 容量小于 1024 的時候,新 slice 容量變成原來的 2 倍;原 slice 容量超過 1024,新 slice 容量變成原來的1.25倍。

07. 參數(shù)傳遞切片和切片指針有什么區(qū)別?

我們都知道切片底層就是一個結(jié)構(gòu)體,里面有三個元素:

type SliceHeader struct {
 Data uintptr
 Len  int
 Cap  int
}

分別表示切片底層數(shù)據(jù)的地址,切片長度,切片容量。

當(dāng)切片作為參數(shù)傳遞時,其實就是一個結(jié)構(gòu)體的傳遞,因為Go語言參數(shù)傳遞只有值傳遞,傳遞一個切片就會淺拷貝原切片,但因為底層數(shù)據(jù)的地址沒有變,所以在函數(shù)內(nèi)對切片的修改,也將會影響到函數(shù)外的切片,舉例:

func modifySlice(s []string)  {
 s[0] = "song"
 s[1] = "Golang"
 fmt.Println("out slice: ", s)
}

func main()  {
 s := []string{"asong", "Golang夢工廠"}
 modifySlice(s)
 fmt.Println("inner slice: ", s)
}
// 運行結(jié)果
out slice:  [song Golang]
inner slice:  [song Golang]

不過這也有一個特例,先看一個例子:

func appendSlice(s []string)  {
 s = append(s, "快關(guān)注?。?)
 fmt.Println("out slice: ", s)
}

func main()  {
 s := []string{"asong", "Golang夢工廠"}
 appendSlice(s)
 fmt.Println("inner slice: ", s)
}
// 運行結(jié)果
out slice:  [asong Golang夢工廠 快關(guān)注??!]
inner slice:  [asong Golang夢工廠]

因為切片發(fā)生了擴(kuò)容,函數(shù)外的切片指向了一個新的底層數(shù)組,所以函數(shù)內(nèi)外不會相互影響,因此可以得出一個結(jié)論,當(dāng)參數(shù)直接傳遞切片時,如果指向底層數(shù)組的指針被覆蓋或者修改(copy、重分配、append觸發(fā)擴(kuò)容),此時函數(shù)內(nèi)部對數(shù)據(jù)的修改將不再影響到外部的切片,代表長度的len和容量cap也均不會被修改。

參數(shù)傳遞切片指針就很容易理解了,如果你想修改切片中元素的值,并且更改切片的容量和底層數(shù)組,則應(yīng)該按指針傳遞。

08. range遍歷切片有什么要注意的?

Go語言提供了range關(guān)鍵字用于for 循環(huán)中迭代數(shù)組(array)、切片(slice)、通道(channel)或集合(map)的元素,有兩種使用方式:

for k,v := range _ { }
for k := range _ { }

第一種是遍歷下標(biāo)和對應(yīng)值,第二種是只遍歷下標(biāo),使用range遍歷切片時會先拷貝一份,然后在遍歷拷貝數(shù)據(jù):

s := []int{1, 2}
for k, v := range s {
  
}
會被編譯器認(rèn)為是
for_temp := s
len_temp := len(for_temp)
for index_temp := 0; index_temp < len_temp; index_temp++ {
  value_temp := for_temp[index_temp]
  _ = index_temp
  value := value_temp
  
}

不知道這個知識點的情況下很容易踩坑,例如下面這個例子:

package main

import (
 "fmt"
)

type user struct {
 name string
 age uint64
}

func main()  {
 u := []user{
  {"asong",23},
  {"song",19},
  {"asong2020",18},
 }
 for _,v := range u{
  if v.age != 18{
   v.age = 20
  }
 }
 fmt.Println(u)
}
// 運行結(jié)果
[{asong 23} {song 19} {asong2020 18}]

因為使用range遍歷切片u,變量v是拷貝切片中的數(shù)據(jù),修改拷貝數(shù)據(jù)不會對原切片有影響。

之前寫了一個對for-range踩坑總結(jié),可以讀一下:面試官:go中for-range使用過嗎?這幾個問題你能解釋一下原因嗎?

總結(jié)

本文總結(jié)了8道切片相關(guān)的面試真題,切片一直是面試中的重要考點,把本文這幾個知識點弄會,應(yīng)對面試官就會變的輕松自如。

到此這篇關(guān)于Go語言切片面試??嫉奈恼戮徒榻B到這了,更多相關(guān)Go語言切片面試內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go語言接口之接口值舉例詳解

    go語言接口之接口值舉例詳解

    接口是一種抽象類型,是對其他類型行為的概括與抽象,從語法角度來看,接口是一組方法定義的集合,下面這篇文章主要給大家介紹了關(guān)于go語言接口之接口值的相關(guān)資料,文章通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • Golang中可比較的數(shù)據(jù)類型詳解

    Golang中可比較的數(shù)據(jù)類型詳解

    在日常開發(fā)中,比較操作是最常用的基本操作之一,可以用來判斷變量之間是否相等或者對應(yīng)的大小關(guān)系,本文將深入解析 Golang 中可比較的數(shù)據(jù)類型,并結(jié)合代碼示例來說明如何在不同情況下進(jìn)行比較,需要的朋友可以參考下
    2024-01-01
  • Go語言基礎(chǔ)學(xué)習(xí)教程

    Go語言基礎(chǔ)學(xué)習(xí)教程

    這篇文章主要介紹了Go語言基礎(chǔ)知識,包括基本語法、語句、數(shù)組等的定義與用法,需要的朋友可以參考下
    2016-07-07
  • Go Java算法最大單詞長度乘積示例詳解

    Go Java算法最大單詞長度乘積示例詳解

    這篇文章主要為大家介紹了Go Java算法最大單詞長度乘積示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go語言制作svg格式樹形圖的示例代碼

    Go語言制作svg格式樹形圖的示例代碼

    SVG是可伸縮矢量圖形?(Scalable?Vector?Graphics),于2003年1月14日成為?W3C?推薦標(biāo)準(zhǔn)。本文將利用Go語言實現(xiàn)制作svg格式樹形圖,感興趣的可以了解一下
    2022-09-09
  • 詳解Golang中下劃線的使用方法

    詳解Golang中下劃線的使用方法

    這篇文章主要介紹了詳解Golang中下劃線的使用方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • 基于Golang實現(xiàn)Excel表格的導(dǎo)入導(dǎo)出功能

    基于Golang實現(xiàn)Excel表格的導(dǎo)入導(dǎo)出功能

    最近項目開發(fā)中有涉及到Excel的導(dǎo)入與導(dǎo)出功能,特別是導(dǎo)出表格時需要特定的格式,所以本文給大家介紹了基于Golang實現(xiàn)Excel表格的導(dǎo)入導(dǎo)出功能,文中通過代碼示例和圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • golang json性能分析詳解

    golang json性能分析詳解

    json格式可以算我們?nèi)粘W畛S玫男蛄谢袷街涣?,Go語言作為一個由Google開發(fā),號稱互聯(lián)網(wǎng)的C語言的語言,自然也對JSON格式支持很好。下面這篇文章主要給大家詳細(xì)分析介紹了golang json性能的相關(guān)資料,需要的朋友可以參考下。
    2018-02-02
  • Go語言sort包函數(shù)使用示例

    Go語言sort包函數(shù)使用示例

    這篇文章主要為大家介紹了Go語言sort包函數(shù)使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go 并發(fā)讀寫 sync.map 詳細(xì)

    Go 并發(fā)讀寫 sync.map 詳細(xì)

    閱讀本文你將會明確 sync.Map 和原生 map +互斥鎖/讀寫鎖之間的性能情況。標(biāo)準(zhǔn)庫 sync.Map 雖說支持并發(fā)讀寫 map,但更適用于讀多寫少的場景,因為他寫入的性能比較差,使用時要考慮清楚這一點。
    2021-10-10

最新評論

南岸区| 佛学| 永福县| 库车县| 玉山县| 临湘市| 丹寨县| 隆安县| 兴安盟| 奉节县| 彝良县| 靖江市| 沙田区| 大足县| 陵水| 屏东县| 饶平县| 平潭县| 江孜县| 台前县| 龙岩市| 隆林| 阳曲县| 晋江市| 洪泽县| 湖口县| 泽普县| 桐梓县| 日喀则市| 辰溪县| 永德县| 神农架林区| 乌拉特中旗| 玛纳斯县| 乌兰察布市| 绥阳县| 安宁市| 三江| 景谷| 鹿邑县| 囊谦县|