溫馨提示×

溫馨提示×

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

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

go語言中數(shù)組與切片有哪些區(qū)別

發(fā)布時間:2023-02-07 10:32:29 來源:億速云 閱讀:165 作者:iii 欄目:編程語言

本文小編為大家詳細介紹“go語言中數(shù)組與切片有哪些區(qū)別”,內容詳細,步驟清晰,細節(jié)處理妥當,希望這篇“go語言中數(shù)組與切片有哪些區(qū)別”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

數(shù)組與切片的區(qū)別:1、切片是指針類型,數(shù)組是值類型;2、數(shù)組的賦值形式為值傳遞,切片的賦值形式為引用傳遞;3、數(shù)組的長度是固定的,而切片長度可以任意調整(切片是動態(tài)的數(shù)組);4、數(shù)組的長度是固定的,而切片長度可以任意調整(切片是動態(tài)的數(shù)組)。

undefined

Go語言中的數(shù)組大概相當與C/C++中的數(shù)組,固定大小,不能夠動態(tài)擴展大小,而切片大概相當與C++中的Vector,可以動態(tài)擴展大小,當大小超過容量時,重新分配一塊內存,然后將數(shù)據(jù)復制到新的內存區(qū)域。

一、數(shù)組

Go的切片是在數(shù)組之上的抽象數(shù)據(jù)類型,因此在了解切片之前必須要先理解數(shù)組。

1、數(shù)組的三種申明方式

  • var identifier [len]type

  • var identifier = [len]type{value1, value2, … , valueN}

  • var identifier = […]type{value1, value2, … , valueN}

相對應的:

  • identifier := [len]type{}

  • identifier := [len]type{value1, value2, … , valueN}

  • identifier := […]type{value1, value2, … , valueN}

例子:

var iarray1 [5]int32
var iarray2 [5]int32 = [5]int32{1, 2, 3, 4, 5}
iarray3 := [5]int32{1, 2, 3, 4, 5}
iarray4 := [5]int32{6, 7, 8, 9, 10}
iarray5 := [...]int32{11, 12, 13, 14, 15}
iarray6 := [4][4]int32{{1}, {1, 2}, {1, 2, 3}}  

fmt.Println(iarray1)
fmt.Println(iarray2)
fmt.Println(iarray3)
fmt.Println(iarray4)
fmt.Println(iarray5)
fmt.Println(iarray6)

結果:

[0 0 0 0 0]
[1 2 3 4 5]
[1 2 3 4 5]
[6 7 8 9 10]
[11 12 13 14 15]
[[1 0 0 0] [1 2 0 0] [1 2 3 0] [0 0 0 0]]

我們看數(shù)組 iarray1,只聲明,并未賦值,Go語言幫我們自動賦值為0。再看 iarray2 和 iarray3 ,我們可以看到,Go語言的聲明,可以表明類型,也可以不表明類型,var iarray3 = [5]int32{1, 2, 3, 4, 5} 也是完全沒問題的。

2、數(shù)組的容量和長度

數(shù)組的容量和長度是一樣的。cap() 函數(shù)和 len() 函數(shù)均輸出數(shù)組的容量(即長度)

3、類型

數(shù)組是值類型,將一個數(shù)組賦值給另一個數(shù)組時,傳遞的是一份拷貝。而切片是引用類型,切片包裝的數(shù)組稱為該切片的底層數(shù)組??聪旅娴睦樱?/p>

//a是一個數(shù)組,注意數(shù)組是一個固定長度的,初始化時候必須要指定長度,不指定長度的話就是切片了
a := [3]int{1, 2, 3}
//b是數(shù)組,是a的一份拷貝
b := a
//c是切片,是引用類型,底層數(shù)組是a
c := a[:]
for i := 0; i < len(a); i++ {
a[i] = a[i] + 1
}
//改變a的值后,b是a的拷貝,b不變,c是引用,c的值改變
fmt.Println(a) //[2,3,4]
fmt.Println(b) //[1 2 3]
fmt.Println(c) //[2,3,4]

二、切片

Go語言中,切片是長度可變、容量固定的相同的元素序列。Go語言的切片本質是一個數(shù)組。容量固定是因為數(shù)組的長度是固定的,切片的容量即隱藏數(shù)組的長度。長度可變指的是在數(shù)組長度的范圍內可變。

1、切片的四種創(chuàng)建方式

  • var slice1 = make([]int,5,10)

  • var slice2 = make([]int,5)

  • var slice3 = []int{}

  • var slice4 = []int{1,2,3,4,5}

相對應的:

  • slice1 := make([]int,5,10)

  • slice2 := make([]int,5)

  • slice3 := []int{}

  • slice4 := []int{1,2,3,4,5}

以上對應的輸出

[0 0 0 0 0]
[0 0 0 0 0]
[]
[1 2 3 4 5]

從3)、4)可見,創(chuàng)建切片跟創(chuàng)建數(shù)組唯一的區(qū)別在于 Type 前的“ [] ”中是否有數(shù)字,為空,則代表切片,否則則代表數(shù)組。因為切片是長度可變的

2、隱藏數(shù)組

Go的切片是在數(shù)組之上的抽象數(shù)據(jù)類型,所以創(chuàng)建的切片始終都有一個數(shù)組存在。

舉例說明:

slice0 := []string{"a", "b", "c", "d", "e"}
slice1 := slice0[2 : len(slice0)-1]
slice2 := slice0[:3]
fmt.Println(slice0, slice1, slice2)
slice2[2] = "8"
fmt.Println(slice0, slice1, slice2)

輸出:

[a b c d e] [c d] [a b c]
[a b 8 d e] [8 d] [a b 8]

也說明,切片slice0 、 slice1 和 slice2是同一個底層數(shù)組的引用,所以slice2改變了,其他兩個都會變

3、append追加切片

內置函數(shù)append可以向一個切片后追加一個或多個同類型的其他值。如果追加的元素數(shù)量超過了原切片容量,那么最后返回的是一個全新數(shù)組中的全新切片。如果沒有超過,那么最后返回的是原數(shù)組中的全新切片。無論如何,append對原切片無任何影響。

舉例說明:

slice1 := make([]int, 2, 5)
fmt.Println(len(slice1), cap(slice1))
for k := range slice1{
   fmt.Println(&slice1[k])
}
slice1 = append(slice1,4)
fmt.Println(len(slice1), cap(slice1))
for k := range slice1{
   fmt.Println(&slice1[k])
}
slice1 = append(slice1,5,6,7)
fmt.Println(len(slice1), cap(slice1))
for k := range slice1{
   fmt.Println(&slice1[k])
}

輸出:

2 5 //長度和容量
0xc420012150
0xc420012158

3 5 //第一次追加,未超出容量,所以內存地址未發(fā)生改變
0xc420012150
0xc420012158
0xc420012160

6 10 //第二次追加,超過容量,內存地址都發(fā)生了改變,且容量也發(fā)生了改變,且是原來的2倍
0xc4200100f0
0xc4200100f8
0xc420010100
0xc420010108
0xc420010110
0xc420010118

再看一個例子:

slice1 := make([]int, 2, 5)
slice2 := slice1[:1]
fmt.Println(len(slice1), cap(slice1))
for k := range slice1{
   fmt.Println(&slice1[k])
}
fmt.Println(len(slice2), cap(slice2))
for k := range slice2{
   fmt.Println(&slice2[k])
}

slice2 = append(slice2,4,5,6,7,8)

fmt.Println(len(slice1), cap(slice1))
for k := range slice1{
   fmt.Println(&slice1[k])
}
fmt.Println(len(slice2), cap(slice2))
for k := range slice2{
   fmt.Println(&slice2[k])
}

以上輸出:

2 5 //slice1的長度和容量
0xc4200700c0
0xc4200700c8

1 5  //slice2的長度和容量
0xc4200700c0

2 5 //slice2追加后,slice1的長度和容量、內存都未發(fā)生改變
0xc4200700c0
0xc4200700c8

6 10 //slice2追加后,超過了容量,所以slice2的長度和容量、內存地址都發(fā)生改變。
0xc42007e000
0xc42007e008
0xc42007e010
0xc42007e018
0xc42007e020
0xc42007e028

三、GO中數(shù)組與切片的區(qū)別

下面的主要看一些實際的例子來說明數(shù)組和切片的區(qū)別。

1、數(shù)組與切片的賦值形式

例子1

arr1 := [3] int {1,2,3}
arr2 := arr1
for k := range arr1 {
   fmt.Printf("%v ",&arr1[k]);
}

fmt.Println("");

for k := range arr2 {
   fmt.Printf("%v ",&arr2[k]);
}

fmt.Println("\n=================");

slice1 := [] int{1,2,3}
slice2 := slice1
for k := range slice1 {
   fmt.Printf("%v ",&slice1[k]);
}
fmt.Println("");
for k := range slice2 {
   fmt.Printf("%v ",&slice2[k]);
}

輸出結果:
0xc420014140 0xc420014148 0xc420014150
0xc420014160 0xc420014168 0xc420014170
=================
0xc4200141a0 0xc4200141a8 0xc4200141b0
0xc4200141a0 0xc4200141a8 0xc4200141b0

從這個例子中可以看出,數(shù)組的賦值是值的拷貝,是一個全新的數(shù)組。而切片的賦值是引用。下面再來看一個例子。

例子2:

arr1 := [3] int {1,2,3}
arr2 := arr1
fmt.Printf("%v %v ",arr1,arr2);

arr1[0] = 11
arr2[1] = 22
fmt.Printf("\n%v %v ",arr1,arr2);

fmt.Println("\n================");

slice1 := [] int{1,2,3}
slice2 := slice1
fmt.Printf("%v %v ",slice1,slice2);

slice1[0] = 11
slice2[1] = 22
fmt.Printf("\n%v %v ",slice1,slice2);

輸出結果:
[1 2 3] [1 2 3]
[11 2 3] [1 22 3]
================
[1 2 3] [1 2 3]
[11 22 3] [11 22 3]

這個例子再一次說明:數(shù)組是賦值是copy,而切片僅是引用。例子1和例子2中的切片的聲明用到了隱藏數(shù)組。再來看下例子3,使用非隱藏數(shù)組的情況。

例子3:

arr1 := [5] int {1,2,3}
slice1 := arr1[0:3]
slice2 := slice1[0:4]

fmt.Printf("len: %d cap: %d %v\n",len(arr1),cap(arr1),arr1); //打印出非隱藏數(shù)組
for k := range arr1 {
   fmt.Printf("%v ",&arr1[k]);
}

fmt.Println("");

fmt.Printf("len: %d cap: %d %v\n",len(slice1),cap(slice1),slice1); //打印切片1
for k := range slice1 {
   fmt.Printf("%v ",&slice1[k]);
}

fmt.Println("");

fmt.Printf("len: %d cap: %d %v\n",len(slice2),cap(slice2),slice2); //打印切片2
for k := range slice2 {
   fmt.Printf("%v ",&slice2[k]);
}
fmt.Println("\n=================");

arr1[0] = 11   //非隱藏數(shù)組、切片1、切片2各自發(fā)生更改
slice1[1] = 22
slice2[2] = 33

fmt.Printf("len: %d cap: %d %v\n",len(arr1),cap(arr1),arr1); //再次打印非隱藏數(shù)組
for k := range arr1 {
   fmt.Printf("%v ",&arr1[k]);
}

fmt.Println("");

fmt.Printf("len: %d cap: %d %v\n",len(slice1),cap(slice1),slice1); //再此打印切片1
for k := range slice1 {
   fmt.Printf("%v ",&slice1[k]);
}
fmt.Println("");

fmt.Printf("len: %d cap: %d %v\n",len(slice2),cap(slice2),slice2); //再次打印切片2
for k := range slice2 {
   fmt.Printf("%v ",&slice2[k]);
}

輸出結果:
len: 5 cap: 5 [1 2 3 0 0]0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170
len: 3 cap: 5 [1 2 3]0xc420012150 0xc420012158 0xc420012160 len: 4 cap: 5 [1 2 3 0]0xc420012150 0xc420012158 0xc420012160 0xc420012168
=================
len: 5 cap: 5 [11 22 33 0 0]
0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170
len: 3 cap: 5 [11 22 33]
0xc420012150 0xc420012158 0xc420012160
len: 4 cap: 5 [11 22 33 0]
0xc420012150 0xc420012158 0xc420012160 0xc42001216

綜上三個例子,可以看出切片是數(shù)組的引用,這里包括對隱藏數(shù)組、非隱藏數(shù)組引用。

2、數(shù)組作為參數(shù),被函數(shù)調用

func Connect() {
   arr1 := [5] int {1,2,3}
   fmt.Printf("%v ",arr1);
   for k := range arr1 {
       fmt.Printf("%v ",&arr1[k]);
   }
   fmt.Println("");
   f1(arr1)
   fmt.Println("");
   f2(&arr1)
}
func f1(arr [5]int) {
   fmt.Printf("%v ",arr);
   for k := range arr {
       fmt.Printf("%v ",&arr[k]);
   }
}
func f2(arr *[5]int) {
   fmt.Printf("%v ",arr);
   for k := range arr {
       fmt.Printf("%v ",&arr[k]);
   }
}

輸出結果:
[1 2 3 0 0]  0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170
[1 2 3 0 0]  0xc4200121b0 0xc4200121b8 0xc4200121c0 0xc4200121c8 0xc4200121d0
&[1 2 3 0 0] 0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170

從上面的例子可以看出,數(shù)組在參數(shù)中,可以使用值傳遞和引用傳遞。值傳遞會拷貝新數(shù)組,引用傳遞則使用原數(shù)組。

func Connect() {
   slice1 := [] int {1,2,3}
   fmt.Printf("%v ",slice1);
   for k := range slice1 {
       fmt.Printf("%v ",&slice1[k]);
   }
   fmt.Println("");
   f1(slice1)
}
func f1(slice []int) {
   fmt.Printf("%v ",slice);
   for k := range slice {
       fmt.Printf("%v ",&slice[k]);
   }
}

輸出結果:
[1 2 3] 0xc420014140 0xc420014148 0xc420014150
[1 2 3] 0xc420014140 0xc420014148 0xc42001415

從這個例子中可以看出,切片在參數(shù)中傳遞本身就引用。

讀到這里,這篇“go語言中數(shù)組與切片有哪些區(qū)別”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

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

AI