欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談golang slice 切片原理

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

slice介紹

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

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

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

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

slice聲明

聲明slice時方括號[]內沒有任何數(shù)據(jù)
聲明一個元素類型為int的slice
var mySlice []int    聲明兩個元素類型為byte的slice   

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

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

這個是 slice 的數(shù)據(jù)結構,它很簡單,一個指向真實 array 地址的指針 ptr ,slice 的長度 len 和容量 cap 。

其中 len 和 cap 就是我們在調用 len(slice) 和 cap(slice) 返回的值。

我們來按照 slice 的數(shù)據(jù)結構定義來解析出 ptr, len, cap

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

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

package main

import (
  "fmt"
  "unsafe"
)

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

// 因為需要指針計算,所以需要獲取int的長度
// 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轉換成自定義的 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

  // 轉成數(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))
}

運行一下查看結果

$ 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內容,和自定義的 Slice 的值,還有按照 slice 中的指針指向的內存,就是實際 Array 數(shù)據(jù)。當修改了 slice 中的len, len(s) 也變了。

接下來結合幾個例子,了解下slice一些用法

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

這是一個 append 的測試

// 每次cap改變,指向array的ptr就會變化一次
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)

運行結果

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]

看出來了吧,每次cap改變的時候指向array內存的指針都在變化。當在使用 append 的時候,如果 cap==len 了這個時候就會新開辟一塊更大內存,然后把之前的數(shù)據(jù)復制過去。

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

在實際使用中,我們最好事先預期好一個cap,這樣在使用append的時候可以避免反復重新分配內存復制之前的數(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)

運行結果

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

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

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

知道了切片原理就可以在開發(fā)中避免出現(xiàn)錯誤了,希望這篇博客可以給大家?guī)韼椭R蚕M蠹叶喽嘀С帜_本之家。

相關文章

  • 使用Go+GoQuery庫實現(xiàn)頭條新聞采集

    使用Go+GoQuery庫實現(xiàn)頭條新聞采集

    在本文中,我們將介紹如何使用Go語言和GoQuery庫實現(xiàn)一個簡單的爬蟲程序,用于抓取頭條新聞的網(wǎng)頁內容,我們還將使用爬蟲代理服務,提高爬蟲程序的性能和安全性,我們將使用多線程技術,提高采集效率,最后,我們將展示爬蟲程序的運行結果和代碼,需要的朋友可以參考下
    2023-10-10
  • go項目中環(huán)境變量的配置

    go項目中環(huán)境變量的配置

    本文主要介紹了go項目中環(huán)境變量的配置,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧
    2021-07-07
  • 一文帶你了解Golang中的緩沖區(qū)Buffer

    一文帶你了解Golang中的緩沖區(qū)Buffer

    作為一種常見的數(shù)據(jù)結構,緩沖區(qū)(Buffer)在計算機科學中有著廣泛的應用。這篇文章將詳細介紹?Go?中?Buffer?的用法,從多個方面介紹其特性和應用場景,需要的可以參考一下
    2023-05-05
  • goland把go項目打包進docker鏡像的全過程記錄

    goland把go項目打包進docker鏡像的全過程記錄

    golang編譯的應用是不需要依賴其他運行環(huán)境的,下面這篇文章主要給大家介紹了關于goland把go項目打包進docker鏡像的相關資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • Go語言range關鍵字循環(huán)時的坑

    Go語言range關鍵字循環(huán)時的坑

    今天小編就為大家分享一篇關于Go語言range關鍵字循環(huán)時的坑,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Golang中指針的使用詳解

    Golang中指針的使用詳解

    Golang是一門支持指針的編程語言,指針是一種特殊的變量,存儲了其他變量的地址。通過指針,可以在程序中直接訪問和修改變量的值,避免了不必要的內存拷貝和傳遞。Golang中的指針具有高效、安全的特點,在并發(fā)編程和底層系統(tǒng)開發(fā)中得到廣泛應用
    2023-04-04
  • Go語言中的網(wǎng)絡編程實現(xiàn)方式

    Go語言中的網(wǎng)絡編程實現(xiàn)方式

    Go語言作為一種簡潔而強大的編程語言,在網(wǎng)絡編程方面表現(xiàn)尤為出色,其內置的net包提供了豐富的網(wǎng)絡I/O基礎設施,支持TCP、UDP協(xié)議,以及DNS解析等功能,本文將結合實際案例,詳細介紹Go語言在網(wǎng)絡編程中的詳細用法,需要的朋友可以參考下
    2024-10-10
  • Go語言使用singleflight解決緩存擊穿

    Go語言使用singleflight解決緩存擊穿

    在構建高性能的服務時,緩存是優(yōu)化數(shù)據(jù)庫壓力和提高響應速度的關鍵技術,但使用緩存也會帶來一些問題,其中就包括緩存擊穿,下面我們就來看看Go語言中如何使用singleflight解決緩存擊穿問題吧
    2024-03-03
  • Golang的性能優(yōu)化和調試技巧

    Golang的性能優(yōu)化和調試技巧

    在開發(fā)和部署Go語言應用程序時,性能優(yōu)化和調試是至關重要的,篇博客將介紹一些常用的Golang性能優(yōu)化和調試技巧,幫助您提升應用程序的性能并解決潛在的問題
    2023-05-05
  • Go語言并發(fā)之WaitGroup的用法詳解

    Go語言并發(fā)之WaitGroup的用法詳解

    這篇文章主要詳細介紹了Go語言并發(fā)中得到WaitGroup,文中有相關的代碼示例供大家參考,對我們的學習或工作有一定的參考價值,感興趣的同學跟著小編一起來學習吧
    2023-06-06

最新評論