欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

golang中的[]*T、*[]T和*[]*T分別是什么

這篇文章主要講解了“golang中的[]*T、*[]T和*[]*T分別是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“golang中的[]*T、*[]T和*[]*T分別是什么”吧!

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到蒙陰網(wǎng)站設(shè)計(jì)與蒙陰網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名與空間、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋蒙陰地區(qū)。

最近看到一段十分詭異的代碼,包含了“[]*T”“*[]T”和“*[]*T”,乍一看都是一樣的,但我們仔細(xì)觀察就發(fā)現(xiàn)他們的不同之處。今天我們就來(lái)介紹一下golong的“[]*T”“*[]T”和“*[]*T”,了解一下他們之間的不同,一起來(lái)看看

作為一個(gè) Go 語(yǔ)言新手,看到一切”詭異“的代碼都會(huì)感到好奇;比如我最近看到的幾個(gè)方法;偽代碼如下:

func FindA() ([]*T,error) {
}

func FindB() ([]T,error) {
}

func SaveA(data *[]T) error {
}

func SaveB(data *[]*T) error {
}

相信大部分剛?cè)腴T Go 的新手看到這樣的代碼也是一臉懵逼,其中最讓人疑惑的就是:

[]*T
*[]T
*[]*T

這樣對(duì)切片的聲明,先不看后面兩種寫法;單獨(dú)看 []*T 還是很好理解的:
該切片中存放的是所有 T 的內(nèi)存地址,會(huì)比存放 T 本身來(lái)說(shuō)要更省空間,同時(shí) []*T 在方法內(nèi)部是可以修改 T 的值,而[]T 是修改不了。

func TestSaveSlice(t *testing.T) {
    a := []T{{Name: "1"}, {Name: "2"}}
    for _, t2 := range a {
        fmt.Println(t2)
    }
    _ = SaveB(a)
    for _, t2 := range a {
        fmt.Println(t2)
    }

}
func SaveB(data []T) error {
    t := data[0]
    t.Name = "1233"
    return nil
}

type T struct {
    Name string
}

比如以上例子打印的是

{1}
{2}
{1}
{2}

只有將方法修改為

func SaveB(data []*T) error {
    t := data[0]
    t.Name = "1233"
    return nil
}

才能修改 T 的值:

&{1}
&{2}
&{1233}
&{2}

示例

下面重點(diǎn)來(lái)看看 []*T 與 *[]T 的區(qū)別,這里寫了兩個(gè) append 函數(shù):

func TestAppendA(t *testing.T) {
    x:=[]int{1,2,3}
    appendA(x)
    fmt.Printf("main %v\n", x)
}
func appendA(x []int) {
    x[0]= 100
    fmt.Printf("appendA %v\n", x)
}

先看第一種,輸出是結(jié)果是:

appendA [1000 2 3]
main [1000 2 3]

說(shuō)明在函數(shù)傳遞過(guò)程中,函數(shù)內(nèi)部的修改能夠影響到外部。


下面我們?cè)倏匆粋€(gè)例子:

func appendB(x []int) {
    x = append(x, 4)
    fmt.Printf("appendA %v\n", x)
}

最終結(jié)果卻是:

appendA [1 2 3 4]
main [1 2 3]

沒(méi)有影響到外部。

而當(dāng)我們?cè)僬{(diào)整一下會(huì)發(fā)現(xiàn)又有所不同:

func TestAppendC(t *testing.T) {
    x:=[]int{1,2,3}
    appendC(&x)
    fmt.Printf("main %v\n", x)
}
func appendC(x *[]int) {
    *x = append(*x, 4)
    fmt.Printf("appendA %v\n", x)
}

最終的結(jié)果:

appendA &[1 2 3 4]
main [1 2 3 4]

可以發(fā)現(xiàn)如果傳遞切片的指針時(shí),使用 append 函數(shù)追加數(shù)據(jù)時(shí)會(huì)影響到外部。

slice 原理

在分析上面三種情況之前,我們先來(lái)了解下 slice 的數(shù)據(jù)結(jié)構(gòu)。

直接查看源碼會(huì)發(fā)現(xiàn) slice 其實(shí)就是一個(gè)結(jié)構(gòu)體,只是不能直接對(duì)外訪問(wèn)。

源碼地址 runtime/slice.go

其中有三個(gè)重要的屬性:

屬性含義
array底層存放數(shù)據(jù)的數(shù)組,是一個(gè)指針。
len切片長(zhǎng)度
cap切片容量 cap>=len

提到切片就不得不想到數(shù)組,可以這么理解:

切片是對(duì)數(shù)組的抽象,而數(shù)組則是切片的底層實(shí)現(xiàn)。

其實(shí)通過(guò)切片這個(gè)名字也不難看出,它就是從數(shù)組中切了一部分;相對(duì)于數(shù)組的固定大小,切片可以根據(jù)實(shí)際使用情況進(jìn)行擴(kuò)容。

所以切片也可以通過(guò)對(duì)數(shù)組"切一刀"獲得:

x1:=[6]int{0,1,2,3,4,5}
x2 := x[1:4]
fmt.Println(len(x2), cap(x2))

其中 x1 的長(zhǎng)度與容量都是6。

x2 的長(zhǎng)度與容量則為3和5。

  • x2 的長(zhǎng)度很容易理解。

  • 容量等于5可以理解為,當(dāng)前這個(gè)切片最多可以使用的長(zhǎng)度。

因?yàn)榍衅?x2 是對(duì)數(shù)組 x1 的引用,所以底層數(shù)組排除掉左邊一個(gè)沒(méi)有被引用的位置則是該切片最大的容量,也就是5。

同一個(gè)底層數(shù)組

以剛才的代碼為例:

func TestAppendA(t *testing.T) {
    x:=[]int{1,2,3}
    appendA(x)
    fmt.Printf("main %v\n", x)
}
func appendA(x []int) {
    x[0]= 100
    fmt.Printf("appendA %v\n", x)
}

在函數(shù)傳遞過(guò)程中,main 中的 x 與 appendA 函數(shù)中的 x 切片所引用的是同個(gè)數(shù)組。

所以在函數(shù)中對(duì) x[0]=100,main函數(shù)中也能獲取到。

本質(zhì)上修改的就是同一塊內(nèi)存數(shù)據(jù)。

值傳遞帶來(lái)的誤會(huì)

在上述例子中,在 appendB 中調(diào)用 append 函數(shù)追加數(shù)據(jù)后會(huì)發(fā)現(xiàn) main 函數(shù)中并沒(méi)有受到影響,這里我稍微調(diào)整了一下示例代碼:

func TestAppendB(t *testing.T) {
    //x:=[]int{1,2,3}
    x := make([]int, 3,5)
    x[0] = 1
    x[1] = 2
    x[2] = 3
    appendB(x)
    fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))
}
func appendB(x []int) {
    x = append(x, 444)
    fmt.Printf("appendB %v len=%v,cap=%v\n", x,len(x),cap(x))
}
主要是修改了切片初始化方式,使得容量大于了長(zhǎng)度,具體原因后續(xù)會(huì)說(shuō)明。

輸出結(jié)果如下:

appendB [1 2 3 444] len=4,cap=5
main [1 2 3] len=3,cap=5

main 函數(shù)中的數(shù)據(jù)看樣子確實(shí)沒(méi)有受到影響;但細(xì)心的朋友應(yīng)該會(huì)注意到  appendB 函數(shù)中的 x 在 append() 之后長(zhǎng)度 +1 變?yōu)榱?。

而在 main 函數(shù)中長(zhǎng)度又變回了3.

這個(gè)細(xì)節(jié)區(qū)別就是為什么 append() "看似" 沒(méi)有生效的原因;至于為什么要說(shuō)“看似”,再次調(diào)整了代碼:

func TestAppendB(t *testing.T) {
    //x:=[]int{1,2,3}
    x := make([]int, 3,5)
    x[0] = 1
    x[1] = 2
    x[2] = 3
    appendB(x)
    fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))

    y:=x[0:cap(x)]
    fmt.Printf("y %v len=%v,cap=%v\n", y,len(y),cap(y))
}

在剛才的基礎(chǔ)之上,以 append 之后的 x 為基礎(chǔ)再做了一個(gè)切片;該切片的范圍為 x 所引用數(shù)組的全部數(shù)據(jù)。

再來(lái)看看執(zhí)行結(jié)果如何:

appendB [1 2 3 444] len=4,cap=5
main [1 2 3] len=3,cap=5
y [1 2 3 444 0] len=5,cap=5

會(huì)神奇的發(fā)現(xiàn) y 將所有數(shù)據(jù)都打印出來(lái),在 appendB 函數(shù)中追加的數(shù)據(jù)其實(shí)已經(jīng)寫入了數(shù)組中,但為什么 x 本身沒(méi)有獲取到呢?

看圖就很容易理解了:

  • 在appendB中確實(shí)是對(duì)原始數(shù)組追加了數(shù)據(jù),同時(shí)長(zhǎng)度也增加了。

  • 但由于是值傳遞,所以 slice 這個(gè)結(jié)構(gòu)體即便是修改了長(zhǎng)度為4,也只是對(duì)復(fù)制的那個(gè)對(duì)象修改了長(zhǎng)度,main 中的長(zhǎng)度依然為3.

  • 由于底層數(shù)組是同一個(gè),所以基于這個(gè)底層數(shù)組重新生成了一個(gè)完整長(zhǎng)度的切片便能看到追加的數(shù)據(jù)了。

所以這里本質(zhì)的原因是因?yàn)?slice 是一個(gè)結(jié)構(gòu)體,傳遞的是值,不管方法里如何修改長(zhǎng)度也不會(huì)影響到原有的數(shù)據(jù)(這里指的是長(zhǎng)度和容量這兩個(gè)屬性)。

切片擴(kuò)容

還有一個(gè)需要注意:

剛才特意提到這里的例子稍有改變,主要是將切片的容量設(shè)置超過(guò)了數(shù)組的長(zhǎng)度;

如果不做這個(gè)特殊設(shè)置會(huì)怎么樣呢?

func TestAppendB(t *testing.T) {
    x:=[]int{1,2,3}
    //x := make([]int, 3,5)
    x[0] = 1
    x[1] = 2
    x[2] = 3
    appendB(x)
    fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))

    y:=x[0:cap(x)]
    fmt.Printf("y %v len=%v,cap=%v\n", y,len(y),cap(y))
}
func appendB(x []int) {
    x = append(x, 444)
    fmt.Printf("appendB %v len=%v,cap=%v\n", x,len(x),cap(x))
}

輸出結(jié)果:

appendB [1 2 3 444] len=4,cap=6
main [1 2 3] len=3,cap=3
y [1 2 3] len=3,cap=3

這時(shí)會(huì)發(fā)現(xiàn) main 函數(shù)中的 y 切片數(shù)據(jù)也沒(méi)有發(fā)生變化,這是為什么呢?

這是因?yàn)槌跏蓟?x 切片時(shí)長(zhǎng)度和容量都為3,當(dāng)在 appendB 函數(shù)中追加數(shù)據(jù)時(shí),會(huì)發(fā)現(xiàn)沒(méi)有位置了。

這時(shí)便會(huì)進(jìn)行擴(kuò)容:

  • 將老數(shù)據(jù)復(fù)制一份到新的數(shù)組中。

  • 追加數(shù)據(jù)。

  • 將新的數(shù)據(jù)內(nèi)存地址返回給 appendB 中的 x .

同樣的由于是值傳遞,所以 appendB 中的切片換了底層數(shù)組對(duì) main 函數(shù)中的切片沒(méi)有任何影響,也就導(dǎo)致最終 main 函數(shù)的數(shù)據(jù)沒(méi)有任何變化了。

傳遞切片指針

有沒(méi)有什么辦法即便是在擴(kuò)容時(shí)也能對(duì)外部產(chǎn)生影響呢?

func TestAppendC(t *testing.T) {
    x:=[]int{1,2,3}
    appendC(&x)
    fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))
}
func appendC(x *[]int) {
    *x = append(*x, 4)
    fmt.Printf("appendC %v\n", x)
}

輸出結(jié)果為:

appendC &[1 2 3 4]
main [1 2 3 4] len=4,cap=6

這時(shí)外部的切片就能受到影響了,其實(shí)原因也很簡(jiǎn)單;

剛才也說(shuō)了,因?yàn)?slice 本身是一個(gè)結(jié)構(gòu)體,所以當(dāng)我們傳遞指針時(shí),就和平時(shí)自定義的 struct 在函數(shù)內(nèi)部通過(guò)指針修改數(shù)據(jù)原理相同。

最終在 appendC 中的 x 的指針指向了擴(kuò)容后的結(jié)構(gòu)體,因?yàn)閭鬟f的是 main 函數(shù)中 x 的指針,所以同樣的 main 函數(shù)中的 x 也指向了該結(jié)構(gòu)體。

總結(jié)

所以總結(jié)一下:

  • 切片是對(duì)數(shù)組的抽象,同時(shí)切片本身也是一個(gè)結(jié)構(gòu)體。

  • 參數(shù)傳遞時(shí)函數(shù)內(nèi)部與外部引用的是同一個(gè)數(shù)組,所以對(duì)切片的修改會(huì)影響到函數(shù)外部。

  • 如果發(fā)生擴(kuò)容,情況會(huì)發(fā)生變化,同時(shí)擴(kuò)容會(huì)導(dǎo)致數(shù)據(jù)拷貝;所以要盡量預(yù)估切片大小,避免數(shù)據(jù)拷貝。

  • 對(duì)切片或數(shù)組重新生成切片時(shí),由于共享的是同一個(gè)底層數(shù)組,所以數(shù)據(jù)會(huì)互相影響,這點(diǎn)需要注意。

  • 切片也可以傳遞指針,但場(chǎng)景很少,還會(huì)帶來(lái)不必要的誤解;建議值傳值就好,長(zhǎng)度和容量占用不了多少內(nèi)存。

相信使用過(guò)切片會(huì)發(fā)現(xiàn)非常類似于  Java  中的 ArrayList,同樣是基于數(shù)組實(shí)現(xiàn),也會(huì)擴(kuò)容發(fā)生數(shù)據(jù)拷貝;這樣看來(lái)語(yǔ)言只是上層使用的選擇,一些通用的底層實(shí)現(xiàn)大家都差不多。

這時(shí)我們?cè)倏礃?biāo)題中的 []*T *[]T *[]*T 就會(huì)發(fā)現(xiàn)這幾個(gè)并沒(méi)有什么聯(lián)系,只是看起來(lái)很像容易唬人。

感謝各位的閱讀,以上就是“golang中的[]*T、*[]T和*[]*T分別是什么”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)golang中的[]*T、*[]T和*[]*T分別是什么這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

網(wǎng)站題目:golang中的[]*T、*[]T和*[]*T分別是什么
URL鏈接:http://aaarwkj.com/article8/peggip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開(kāi)發(fā)、網(wǎng)站設(shè)計(jì)、定制網(wǎng)站云服務(wù)器、企業(yè)網(wǎng)站制作、網(wǎng)站改版

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
国产高清大片一级黄色| 亚洲一区二区三区无人区| 五月天丁香婷婷深爱| 最新在线中文字幕av不卡| 精品熟妇人妻一区二区三区| 精品国产一区二区成人| 国产精品久久护士96| 国产超大超粗超爽视频| 日本女优久久精品观看| 蜜臀av网站在线播放| 最新国产激情福利网站| 夜夜高潮夜夜爽免费观看| 免费观看国产裸体视频| 中文字幕av日韩在线| 日韩伦理高清在线观看| 亚洲av欧美日韩国产| 中文字幕人成乱码在线观看| 久热精品视频在线观看| 亚洲精品理论片在线观看| 日韩精品人成在线播放| 高颜值美女后入内射视频| 国产精品蜜臀av在线一区| 91亚洲精品久久久蜜桃网站| 亚洲美女国产精选999| 国产九色av在线一区尤物| 亚洲精品在线观看日韩欧美| 丁香六月五月色婷婷网| 国产一区中文字幕在线| 国产毛毛片一区二区三区| 天天做日日干夜夜操| 久久精品资源综合网| 把熟睡的少妇弄到高潮| 亚洲欧美日韩之国产综合| 国产姐弟操大率悠荡笕| 97在线观看视频视频| 亚洲一区欧美二区日韩三区| 亚洲精品中文字幕一二三| 91熟女成人精品一区二区| 黄色录像黄色片黄色片| 亚洲中文字幕精品一区二区三区| 青青青在线视频观看华人|