>백엔드 개발 >Golang >Go 언어에서 배열과 슬라이스의 차이점은 무엇입니까?

Go 언어에서 배열과 슬라이스의 차이점은 무엇입니까?

青灯夜游
青灯夜游원래의
2023-01-13 17:26:468277검색

배열과 슬라이스의 차이점: 1. 슬라이스는 포인터 유형이고 배열은 값 유형입니다. 2. 배열의 할당 형식은 값 전송이고 슬라이스의 할당 형식은 참조 전송입니다. , 슬라이스의 길이는 임의로 조정할 수 있습니다(슬라이스는 동적 배열입니다). 4. 배열의 길이는 고정되어 있지만 슬라이스 길이는 임의로 조정할 수 있습니다(슬라이스는 동적 배열입니다).

Go 언어에서 배열과 슬라이스의 차이점은 무엇입니까?

이 튜토리얼의 운영 환경: Windows 7 시스템, GO 버전 1.18, Dell G3 컴퓨터.

Go 언어의 배열은 C/C++의 배열과 거의 동일합니다. 크기가 고정되어 있으며 동적으로 확장할 수 없습니다. 크기가 초과되면 동적으로 확장할 수 있는 C++의 벡터와 거의 동일합니다. 용량이 줄어들면 메모리 블록을 다시 할당하고 데이터를 새 메모리 영역에 복사합니다. golang의 배열과 슬라이스를 더 잘 이해하기 위해 몇 가지 질문을 살펴보겠습니다.

1. 배열

Go의 슬라이스는 배열 위에 있는 추상적인 데이터 타입이므로 슬라이스를 이해하기 전에 먼저 배열을 이해해야 합니다.

1. 배열을 선언하는 세 가지 방법

  • var 식별자 [len]type
  • var 식별자 = [len]type{value1, value2, … , valueN}
  • var 식별자 = […]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)

Result:

[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]]

선언만 되었지만 할당되지 않은 배열 iarray1을 살펴봅니다. Go 언어는 자동으로 값을 0으로 할당합니다. iarray2와 iarray3을 다시 보면 Go 언어 선언이 유형을 나타낼 수 있는지 여부를 알 수 있습니다. var iarray3 = [5]int32{1, 2, 3, 4, 5}도 완전히 괜찮습니다.

2. 어레이의 용량과 길이

어레이의 용량과 길이는 동일합니다. cap() 함수와 len() 함수는 모두 array

3의 용량(예: 길이)을 출력합니다. Type

Array는 값 유형입니다. 한 배열을 다른 배열에 할당하면 복사본이 전달됩니다. 슬라이스는 참조 유형이지만, 슬라이스로 래핑된 배열을 슬라이스의 기본 배열이라고 합니다. 다음 예를 보세요:

//a是一个数组,注意数组是一个固定长度的,初始化时候必须要指定长度,不指定长度的话就是切片了
a := [3]int{1, 2, 3}
//b是数组,是a的一份拷贝
b := a
//c是切片,是引用类型,底层数组是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]

2. 슬라이싱

Go 언어에서 슬라이스는 가변 길이와 고정 용량을 가진 동일한 요소 시퀀스입니다. Go 언어에서 슬라이스의 본질은 배열입니다. 배열의 길이는 고정되어 있으므로 용량은 고정되어 있고, 슬라이스의 용량은 숨겨진 배열의 길이입니다. 가변 길이란 배열 길이 범위 내에서 가변적임을 의미합니다.

1. 슬라이스를 만드는 네 가지 방법

  • 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}

위의 해당 출력은 3), 4 )에서 해당 출력

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

입니다. 슬라이스 생성과 배열 생성의 유일한 차이점은 Type 앞의 "[]"에 숫자가 있는지 여부입니다. 비어 있으면 슬라이스를 나타내고, 그렇지 않으면 배열을 나타냅니다. 슬라이스에는 가변 길이가 있기 때문에

2. 숨겨진 배열

Go의 슬라이스는 배열 위에 있는 추상 데이터 유형이므로 생성된 슬라이스에는 항상 배열이 있습니다.

예:

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]

또한 슬라이스 슬라이스0, 슬라이스1 및 슬라이스2가 동일한 기본 배열에 대한 참조임을 보여줍니다. 따라서 슬라이스2가 변경되면 나머지 두 개도 변경됩니다

3.

내장 함수인 add는 동일한 유형의 다른 값을 하나 이상 슬라이스에 추가할 수 있습니다. 추가된 요소 수가 원래 슬라이스의 용량을 초과하는 경우 최종적으로 반환되는 것은 새로운 배열의 새로운 슬라이스입니다. 초과하지 않으면 최종적으로 반환되는 것은 원래 배열의 새로운 조각입니다. 어떤 경우에도 추가는 원본 슬라이스에 영향을 주지 않습니다.

예:

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 //第一次追加,未超出容量,所以内存地址未发生改变
0xc420012150
0xc420012158
0xc420012160

6 10 //第二次追加,超过容量,内存地址都发生了改变,且容量也发生了改变,且是原来的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的长度和容量、内存都未发生改变
0xc4200700c0
0xc4200700c8

6 10 //slice2追加后,超过了容量,所以slice2的长度和容量、内存地址都发生改变。
0xc42007e000
0xc42007e008
0xc42007e010
0xc42007e018
0xc42007e020
0xc42007e028

3 GO에서 배열과 슬라이스의 차이점

다음은 배열을 설명하기 위한 몇 가지 실제 예를 주로 살펴봅니다. 그리고 슬라이싱의 차이.

1. 배열과 슬라이스의 할당 형태

예제 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

이 예에서 볼 수 있듯이

배열의 할당은 값의 복사본이며, 이는 완전히 새로운 배열입니다. 슬라이스 할당은 참고 입니다. 또 다른 예를 살펴보겠습니다.

예제 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]

이 예는 다시 한 번 설명합니다. 배열은 할당 및 복사본인 반면 슬라이스는 참조일 뿐입니다. 예제 1과 2의 슬라이스 선언은 숨겨진 배열을 사용합니다. 숨겨진 배열이 아닌 배열을 사용하여 예제 3을 다시 살펴보겠습니다.

예제 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); //打印出非隐藏数组
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   //非隐藏数组、切片1、切片2各自发生更改
slice1[1] = 22
slice2[2] = 33

fmt.Printf("len: %d cap: %d %v\n",len(arr1),cap(arr1),arr1); //再次打印非隐藏数组
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

위의 세 가지 예를 요약하면 슬라이스는 숨겨진 배열 및 숨겨진 배열이 아닌 배열에 대한 참조를 포함하여 배열에 대한 참조임을 알 수 있습니다.

2. 배열은 매개변수로 사용되며 함수에 의해 호출됩니다.

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

从上面的例子可以看出,数组在参数中,可以使用值传递和引用传递。值传递会拷贝新数组,引用传递则使用原数组。

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

从这个例子中可以看出,切片在参数中传递本身就引用。

总结:

  • 切片是指针类型,数组是值类型

  • 数组的赋值形式为值传递,切片的赋值形式为引用传递

  • 数组的长度是固定的,而切片长度可以任意调整(切片是动态的数组)

  • 数组只有长度一个属性,而切片比数组多了一个容量(cap)属性

【相关推荐:Go视频教程编程教学

위 내용은 Go 언어에서 배열과 슬라이스의 차이점은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.