溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

golang 中slice和string如何使用

發布時間:2021-07-19 11:10:48 來源:億速云 閱讀:99 作者:Leah 欄目:編程語言

這期內容當中小編將會給大家帶來有關golang 中slice和string如何使用,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

slice 和 string 內部結構

slice 和 string 的內部結構可以在 $GOROOT/src/reflect/value.go 里面找到

type StringHeader struct {
    Data uintptr
    Len  int
}

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

可以看到一個 string 包含一個數據指針和一個長度,長度是不可變的

slice 包含一個數據指針、一個長度和一個容量,當容量不夠時會重新申請新的內存,Data 指針將指向新的地址,原來的地址空間將被釋放

從這些結構就可以看出,string 和 slice 的賦值,包括當做參數傳遞,和自定義的結構體一樣,都僅僅是 Data 指針的淺拷貝

slice 重用

append 操作
si1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
si2 := si1
si2 = append(si2, 0)
Convey("重新分配內存", func() {
    header1 := (*reflect.SliceHeader)(unsafe.Pointer(&si1))
    header2 := (*reflect.SliceHeader)(unsafe.Pointer(&si2))
    fmt.Println(header1.Data)
    fmt.Println(header2.Data)
    So(header1.Data, ShouldNotEqual, header2.Data)
})

si1 和 si2 開始都指向同一個數組,當對 si2 執行 append 操作時,由于原來的 Cap 值不夠了,需要重新申請新的空間,因此 Data 值發生了變化,在 $GOROOT/src/reflect/value.go 這個文件里面還有關于新的 cap 值的策略,在 grow 這個函數里面,當 cap 小于 1024 的時候,是成倍的增長,超過的時候,每次增長 25%,而這種內存增長不僅僅數據拷貝(從舊的地址拷貝到新的地址)需要消耗額外的性能,舊地址內存的釋放對 gc 也會造成額外的負擔,所以如果能夠知道數據的長度的情況下,盡量使用 make([]int, len, cap) 預分配內存,不知道長度的情況下,可以考慮下面的內存重用的方法

內存重用
si1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
si2 := si1[:7]
Convey("不重新分配內存", func() {
    header1 := (*reflect.SliceHeader)(unsafe.Pointer(&si1))
    header2 := (*reflect.SliceHeader)(unsafe.Pointer(&si2))
    fmt.Println(header1.Data)
    fmt.Println(header2.Data)
    So(header1.Data, ShouldEqual, header2.Data)
})

Convey("往切片里面 append 一個值", func() {
    si2 = append(si2, 10)
    Convey("改變了原 slice 的值", func() {
        header1 := (*reflect.SliceHeader)(unsafe.Pointer(&si1))
        header2 := (*reflect.SliceHeader)(unsafe.Pointer(&si2))
        fmt.Println(header1.Data)
        fmt.Println(header2.Data)
        So(header1.Data, ShouldEqual, header2.Data)
        So(si1[7], ShouldEqual, 10)
    })
})

si2 是 si1 的一個切片,從第一段代碼可以看到切片并不重新分配內存,si2 和 si1 的 Data 指針指向同一片地址,而第二段代碼可以看出,當我們往 si2 里面 append 一個新的值的時候,我們發現仍然沒有內存分配,而且這個操作使得 si1 的值也發生了改變,因為兩者本就是指向同一片 Data 區域,利用這個特性,我們只需要讓 si1 = si1[:0] 就可以不斷地清空 si1 的內容,實現內存的復用了

PS: 你可以使用 copy(si2, si1) 實現深拷貝

string

Convey("字符串常量", func() {
    str1 := "hello world"
    str2 := "hello world"
    Convey("地址相同", func() {
        header1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))
        header2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))
        fmt.Println(header1.Data)
        fmt.Println(header2.Data)
        So(header1.Data, ShouldEqual, header2.Data)
    })
})

這個例子比較簡單,字符串常量使用的是同一片地址區域

Convey("相同字符串的不同子串", func() {
    str1 := "hello world"[:6]
    str2 := "hello world"[:5]
    Convey("地址相同", func() {
        header1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))
        header2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))
        fmt.Println(header1.Data, str1)
        fmt.Println(header2.Data, str2)
        So(str1, ShouldNotEqual, str2)
        So(header1.Data, ShouldEqual, header2.Data)
    })
})

相同字符串的不同子串,不會額外申請新的內存,但是要注意的是這里的相同字符串,指的是 str1.Data == str2.Data && str1.Len == str2.Len,而不是 str1 == str2,下面這個例子可以說明 str1 == str2 但是其 Data 并不相同

Convey("不同字符串的相同子串", func() {
    str1 := "hello world"[:5]
    str2 := "hello golang"[:5]
    Convey("地址不同", func() {
        header1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))
        header2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))
        fmt.Println(header1.Data, str1)
        fmt.Println(header2.Data, str2)
        So(str1, ShouldEqual, str2)
        So(header1.Data, ShouldNotEqual, header2.Data)
    })
})

上述就是小編為大家分享的golang 中slice和string如何使用了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女