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

淺談golang slice 切片原理

 更新時(shí)間:2017年11月08日 17:07:59   作者:逝水-無(wú)痕  
這篇文章主要介紹了淺談golang slice 切片原理,詳細(xì)的介紹了golang slice 切片的概念和原理,具有一定的參考價(jià)值,有興趣的可以了解一下

slice介紹

數(shù)組的長(zhǎng)度在定義之后無(wú)法再次修改;數(shù)組是值類型,每次傳遞都將產(chǎn)生一份副本。顯然這種數(shù)據(jù)結(jié)構(gòu)無(wú)法完全滿足開(kāi)發(fā)者的真實(shí)需求。在初始定義數(shù)組時(shí),我們并不知道需要多大的數(shù)組,因此我們就需要“動(dòng)態(tài)數(shù)組”。在Go里面這種數(shù)據(jù)結(jié)構(gòu)叫slice,slice并不是真正意義上的動(dòng)態(tài)數(shù)組,而是一個(gè)引用類型。slice總是指向一個(gè)底層array,slice的聲明也可以像array一樣,只是不需要長(zhǎng)度,它是可變長(zhǎng)的,可以隨時(shí)往slice里面加數(shù)據(jù)。

初看起來(lái),數(shù)組切片就像一個(gè)指向數(shù)組的指針,實(shí)際上它擁有自己的數(shù)據(jù)結(jié)構(gòu),而不僅僅是個(gè)指針。數(shù)組切片的數(shù)據(jù)結(jié)構(gòu)可以抽象為以下3個(gè)變量:

1.一個(gè)指向原生數(shù)組的指針(point):指向數(shù)組中slice指定的開(kāi)始位置;
2.?dāng)?shù)組切片中的元素個(gè)數(shù)(len):即slice的長(zhǎng)度;
3.?dāng)?shù)組切片已分配的存儲(chǔ)空間(cap):也就是slice開(kāi)始位置到數(shù)組的最后位置的長(zhǎng)度。

從底層實(shí)現(xiàn)的角度來(lái)看,數(shù)組切片實(shí)際上仍然使用數(shù)組來(lái)管理元素,基于數(shù)組,數(shù)組切片添加了一系列管理功能,可以隨時(shí)動(dòng)態(tài)擴(kuò)充存放空間,并且可以被隨意傳遞而不會(huì)導(dǎo)致所管理的元素被重復(fù)復(fù)制。

slice聲明

聲明slice時(shí)方括號(hào)[]內(nèi)沒(méi)有任何數(shù)據(jù)
聲明一個(gè)元素類型為int的slice
var mySlice []int    聲明兩個(gè)元素類型為byte的slice   

golang 中的 slice 非常強(qiáng)大,讓數(shù)組操作非常方便高效。在開(kāi)發(fā)中不定長(zhǎng)度表示的數(shù)組全部都是 slice 。但是很多同學(xué)對(duì) slice 的模糊認(rèn)識(shí),造成認(rèn)為golang中的數(shù)組是引用類型,結(jié)果就是在實(shí)際開(kāi)發(fā)中碰到很多坑,以至于出現(xiàn)一些莫名奇妙的問(wèn)題,數(shù)組中的數(shù)據(jù)丟失了。

下面我們就開(kāi)始詳細(xì)理解下 slice ,理解后會(huì)對(duì)開(kāi)發(fā)出高效的程序非常有幫助。

這個(gè)是 slice 的數(shù)據(jù)結(jié)構(gòu),它很簡(jiǎn)單,一個(gè)指向真實(shí) array 地址的指針 ptr ,slice 的長(zhǎng)度 len 和容量 cap 。

其中 len 和 cap 就是我們?cè)谡{(diào)用 len(slice) 和 cap(slice) 返回的值。

我們來(lái)按照 slice 的數(shù)據(jù)結(jié)構(gòu)定義來(lái)解析出 ptr, len, cap

// 按照上圖定義的數(shù)據(jù)結(jié)構(gòu)
type Slice struct {
  ptr  unsafe.Pointer    // Array pointer
  len  int        // slice length
  cap   int        // slice capacity
}

下面寫一個(gè)完整的程序,嘗試把golang中slice的內(nèi)存區(qū)域轉(zhuǎn)換成我們定義的 Slice 進(jìn)行解析

package main

import (
  "fmt"
  "unsafe"
)

// 按照上圖定義的數(shù)據(jù)結(jié)構(gòu)
type Slice struct {
  ptr unsafe.Pointer // Array pointer
  len int      // slice length
  cap int      // slice capacity
}

// 因?yàn)樾枰羔樣?jì)算,所以需要獲取int的長(zhǎng)度
// 32位 int length = 4
// 64位 int length = 8
var intLen = int(unsafe.Sizeof(int(0)))

func main() {
  s := make([]int, 10, 20)

  // 利用指針讀取 slice memory 的數(shù)據(jù)
  if intLen == 4 { // 32位
    m := *(*[4 + 4*2]byte)(unsafe.Pointer(&s))
    fmt.Println("slice memory:", m)
  } else { // 64 位
    m := *(*[8 + 8*2]byte)(unsafe.Pointer(&s))
    fmt.Println("slice memory:", m)
  }

  // 把slice轉(zhuǎn)換成自定義的 Slice struct
  slice := (*Slice)(unsafe.Pointer(&s))
  fmt.Println("slice struct:", slice)
  fmt.Printf("ptr:%v len:%v cap:%v \n", slice.ptr, slice.len, slice.cap)
  fmt.Printf("golang slice len:%v cap:%v \n", len(s), cap(s))

  s[0] = 0
  s[1] = 1
  s[2] = 2

  // 轉(zhuǎn)成數(shù)組輸出
  arr := *(*[3]int)(unsafe.Pointer(slice.ptr))
  fmt.Println("array values:", arr)

  // 修改 slice 的 len
  slice.len = 15
  fmt.Println("Slice len: ", slice.len)
  fmt.Println("golang slice len: ", len(s))
}

運(yùn)行一下查看結(jié)果

$ go run slice.go

slice memory: [0 64 6 32 200 0 0 0 10 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0]
slice struct: &{0xc820064000 10 20}
ptr:0xc820064000 len:10 cap:20
golang slice len:10 cap:20
array values: [0 1 2]
Slice len: 15
golang slice len: 15

看到了,golang slice 的memory內(nèi)容,和自定義的 Slice 的值,還有按照 slice 中的指針指向的內(nèi)存,就是實(shí)際 Array 數(shù)據(jù)。當(dāng)修改了 slice 中的len, len(s) 也變了。

接下來(lái)結(jié)合幾個(gè)例子,了解下slice一些用法

聲明一個(gè)Array通常使用 make ,可以傳入2個(gè)參數(shù),也可傳入3個(gè)參數(shù),第一個(gè)是數(shù)據(jù)類型,第二個(gè)是 len ,第三個(gè)是 cap 。如果不穿入第三個(gè)參數(shù),則 cap=len ,append 可以用來(lái)向數(shù)組末尾追加數(shù)據(jù)。

這是一個(gè) append 的測(cè)試

// 每次cap改變,指向array的ptr就會(huì)變化一次
s := make([]int, 1)

fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))

for i := 0; i < 5; i++ {
  s = append(s, i)
  fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))
}

fmt.Println("Array:", s)

運(yùn)行結(jié)果

len:1 cap: 1 array ptr: 0xc8200640f0
len:2 cap: 2 array ptr: 0xc820064110
len:3 cap: 4 array ptr: 0xc8200680c0
len:4 cap: 4 array ptr: 0xc8200680c0
len:5 cap: 8 array ptr: 0xc82006c080
len:6 cap: 8 array ptr: 0xc82006c080
Array: [0 0 1 2 3 4]

看出來(lái)了吧,每次cap改變的時(shí)候指向array內(nèi)存的指針都在變化。當(dāng)在使用 append 的時(shí)候,如果 cap==len 了這個(gè)時(shí)候就會(huì)新開(kāi)辟一塊更大內(nèi)存,然后把之前的數(shù)據(jù)復(fù)制過(guò)去。

實(shí)際go在append的時(shí)候放大cap是有規(guī)律的。在 cap 小于1024的情況下是每次擴(kuò)大到 2 * cap ,當(dāng)大于1024之后就每次擴(kuò)大到 1.25 * cap 。所以上面的測(cè)試中cap變化是 1, 2, 4, 8

在實(shí)際使用中,我們最好事先預(yù)期好一個(gè)cap,這樣在使用append的時(shí)候可以避免反復(fù)重新分配內(nèi)存復(fù)制之前的數(shù)據(jù),減少不必要的性能消耗。

創(chuàng)建切片

s := []int{1, 2, 3, 4, 5}
fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s), cap(s), *(*unsafe.Pointer)(unsafe.Pointer(&s)))
fmt.Println("Array:", s)

s1 := s[1:3]
fmt.Printf("len:%d cap: %d array ptr: %v \n", len(s1), cap(s1), *(*unsafe.Pointer)(unsafe.Pointer(&s1)))
fmt.Println("Array", s1)

運(yùn)行結(jié)果

len:5 cap: 5 array ptr: 0xc820012210
Array: [1 2 3 4 5]
len:2 cap: 4 array ptr: 0xc820012218
Array [2 3]

在一個(gè)切片基礎(chǔ)上創(chuàng)建新的切片 s1 ,新切片的 ptr 指向的就是 s1[0] 數(shù)據(jù)的內(nèi)存地址??梢钥吹街羔樀刂?0xc820012210 與 0xc820012218 相差 8byte 正好是一個(gè)int類型長(zhǎng)度,cap也相應(yīng)的變?yōu)?

就寫到這里了,總結(jié)一下,切片的結(jié)構(gòu)是指向數(shù)據(jù)的指針,長(zhǎng)度和容量。復(fù)制切片,或者在切片上創(chuàng)建新切片,切片中的指針都指向相同的數(shù)據(jù)內(nèi)存區(qū)域。

知道了切片原理就可以在開(kāi)發(fā)中避免出現(xiàn)錯(cuò)誤了,希望這篇博客可以給大家?guī)?lái)幫助。也希望大家多多支持腳本之家。

相關(guān)文章

  • Go語(yǔ)言實(shí)現(xiàn)Base64、Base58編碼與解碼

    Go語(yǔ)言實(shí)現(xiàn)Base64、Base58編碼與解碼

    本文主要介紹了Base64、Base58編碼與解碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 淺析Golang中的net/http路由注冊(cè)與請(qǐng)求處理

    淺析Golang中的net/http路由注冊(cè)與請(qǐng)求處理

    這篇文章主要為大家詳細(xì)介紹了Golang中的net/http路由注冊(cè)與請(qǐng)求處理的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • Go實(shí)現(xiàn)比較時(shí)間大小

    Go實(shí)現(xiàn)比較時(shí)間大小

    這篇文章主要介紹了Go實(shí)現(xiàn)比較時(shí)間大小的方法和示例,非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下。
    2015-04-04
  • GoLang基礎(chǔ)學(xué)習(xí)之go?test測(cè)試

    GoLang基礎(chǔ)學(xué)習(xí)之go?test測(cè)試

    相信每位編程開(kāi)發(fā)者們應(yīng)該都知道,Golang作為一門標(biāo)榜工程化的語(yǔ)言,提供了非常簡(jiǎn)便、實(shí)用的編寫單元測(cè)試的能力,下面這篇文章主要給大家介紹了關(guān)于GoLang基礎(chǔ)學(xué)習(xí)之go?test測(cè)試的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • Go語(yǔ)言利用接口實(shí)現(xiàn)鏈表插入功能詳解

    Go語(yǔ)言利用接口實(shí)現(xiàn)鏈表插入功能詳解

    這篇文章主要為大家介紹了Go語(yǔ)言中的接口,以及如何利用接口實(shí)現(xiàn)鏈表插入功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-04-04
  • go語(yǔ)言的panic和recover函數(shù)用法實(shí)例

    go語(yǔ)言的panic和recover函數(shù)用法實(shí)例

    今天小編就為大家分享一篇關(guān)于go語(yǔ)言的panic和recover函數(shù)用法實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • go實(shí)現(xiàn)自動(dòng)復(fù)制U盤小工具demo

    go實(shí)現(xiàn)自動(dòng)復(fù)制U盤小工具demo

    這篇文章主要為大家介紹了go實(shí)現(xiàn)自動(dòng)復(fù)制U盤小工具demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • golang包快速生成base64驗(yàn)證碼的方法

    golang包快速生成base64驗(yàn)證碼的方法

    這篇文章主要介紹了golang包快速生成base64驗(yàn)證碼的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • go-micro微服務(wù)domain層開(kāi)發(fā)示例詳解

    go-micro微服務(wù)domain層開(kāi)發(fā)示例詳解

    這篇文章主要為大家介紹了go-micro微服務(wù)domain層開(kāi)發(fā)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • go函數(shù)的參數(shù)設(shè)置默認(rèn)值的方法

    go函數(shù)的參數(shù)設(shè)置默認(rèn)值的方法

    Go語(yǔ)言不直接支持函數(shù)參數(shù)默認(rèn)值,但可以通過(guò)指針、結(jié)構(gòu)體、變長(zhǎng)參數(shù)和選項(xiàng)模式等方法模擬,下面給大家分享幾種方式模擬函數(shù)參數(shù)的默認(rèn)值功能,感興趣的朋友一起看看吧
    2025-01-01

最新評(píng)論

丰县| 刚察县| 普兰县| 馆陶县| 新化县| 繁峙县| 苏尼特右旗| 厦门市| 梧州市| 获嘉县| 舟曲县| 龙井市| 汝南县| 体育| 班戈县| 北流市| 长武县| 桐城市| 乌鲁木齐市| 从化市| 奉贤区| 利川市| 旌德县| 虞城县| 夏邑县| 廊坊市| 嘉兴市| 岳西县| 永城市| 济宁市| 白城市| 合江县| 奉节县| 金寨县| 故城县| 马边| 南通市| 南雄市| 丰宁| 海门市| 临夏市|