Slice and Array

2020. 3. 30. 17:30golang/golang-grammar

이전에 정리한 적이 있지만 조금 부족한 것 같아 아래의 사이트를 통해 정리하려고 합니다.

https://blog.golang.org/slices-intro

 

Go Slices: usage and internals - The Go Blog

Andrew Gerrand 5 January 2011 Introduction Go's slice type provides a convenient and efficient means of working with sequences of typed data. Slices are analogous to arrays in other languages, but have some unusual properties. This article will look at wha

blog.golang.org

Array 

Slice type은 array의 추상적인 개념이고 slice를 이해하기 위해선 Array를 먼저 알필요가 있어서 정리한다.

 

Array는 type과 길이를 열거합니다. 예를 들어 [4]int 의 경우 4개의 int 형을 저장할 수 있는 배열이다. 배열의 크기는 고정적입니다. 일반적인 방법으로 배열의 값들을 index를 통해 가져올 수 있습니다. 

var a int[4]
a[0] = 1
i:=a[0]

배열을 굳이 다 초기화 할 필요가 없습니다. 0으로 초기화 되어 있습니다.

 

메모리 안 int[4]

go의 배열은 value입니다. 배열 변수는 전체 배열을 가리킵니다. (이는 C언어와 같이 첫 번째 element를 pointer를 통해 가리키는 것과 다릅니다.) 이와 같은 특징은 배열을 할당하거나 넘겨줄 때 복사본을 만든다는 의미입니다.

 

복사를 피하기 위해서는 pointer를 넘겨줄 수 있습니다. 그러나 pointer는 배열을 가리키고 있습니다. 이러한 pointer의 경우 array가 아닙니다. 그래서 배열을 생각하기 위한 한가지 방법은 named filed보다닌 index와 함께인 일종의 구조체로 생각하는 겁니다. 고정된 크기의 값

 

array는 아래와 같이 나열하여 적을 수 있습니다.

b := [2]string{"Penn", "Teller"}

또는 Compiler가 element의 수를 세려서 크기를 지정할 수 있습니다.

b := [...]string{"Penn", "Teller"}

위 두 경우 모두 사이즈가 2인 배열이 만들어집니다. 

 

Slice

Array의 경우 자신의 공간을 갖고있다. 그리고 크기 등이 고정되어 있어서 융통성이 없어서 go Code에서는 많이 사용되지 않고 Slice가 많이 사용된다. 배열과 비슷하게 만들지만 편의성과 좋은 기능등이 있다.

 

type을 상술한다 []T T의 경우 Slice의 타입이다. 하지만 배열과 다르게 명확한 크기를 지정하지 않는다.

 

Slice는 배열의 크기를 제외한 배열과 동일하게 선언한다. 

letters := []string{"a", "b", "c"}

또 make라는 함수를 사용하여 Slice를 만들 수 있습니다. 

func make([]T, len, cap) []T

T는 해당 Slice의 타입을 지정한다. make 함수는 length와 선택적으로 capacity를 지정받는다. make가 호출되면 첫번재 파라미터의 값을 참조하여 array에 할당하고 slice를 return한다. 

var s []byte
s = make([]byte, 5, 5)

capacity의 경우 생략할 수 있습니다. default로는 length의 길이 만큼 설정됩니다. 

 

동일한 코드의 간결한 버전

s := make(x, 5)

len과 cap은 기본함수를 통해 참조할 수 있습니다.

len(s) == 5
cap(s) == 5

slice 초기 값은 nil입니다. nil Slice의 경우 len과 cap은 0을 반환합니다. 

 

또한 배열과 Slice에 Slicing을 할 수 있습니다. []안에 두개의 인덱스를 :으로 분리하여 Slicing이 됩니다. 

b := []byte{'g', 'o', 'l', 'a', 'n', 'g'}
b[1:4] == []byte{'o', 'l', 'a'}

 

기존의 배열을 통해 Slice를 만들 수 있습니다. 

var x = [4]int{1, 2, 3, 4}
t := x[:]

 

Slice Internals

Slice는 세그먼트의 길이, 배열을 가리키는 pointer, 그리고 capacity로 이루어져있습니다. 

만약 변수 s가 make([]byte, 5)로 만들어지면 아래와 같이 만들어집니다.

 

length는 slice에 조회되는 element의 수입니다. capacity의 경우 기본이 되는 배열안에 있는 element의 수이다. length와 capacity를 명백히 구분하기 위해서 아래의 예제 몇개를 확인하겠습니다.

 

우리가 slice 할 때 slice data 구조 변화와 기본이 되는 배열의 관계의 변화를 관찰하다. 

s = s[2:4]

Slicing은 Slice의 데이터를 copy하지 않는다. 원래의 array를 가리키는 새로운 slice value를 만듭니다. 이런 방식으로 만드는 것은 array indexing을 더 효율적으로 다룰 수 있습니다. slice의 값을 변경시키면 가리키고 있는 배열의 값도 변경됩니다.

d := []byte{'r', 'o', 'a', 'd'}
e := d[2:]
// e == []byte{'a', 'd'}
e[1] = 'm'
// e == []byte{'a', 'm'}
// d == []byte{'r', 'o', 'a', 'm'}

slice의 경우 capacity를 넘어서 배열이 커지지 않는다. capacity를 넘어서 시도하게 된다면 run time오류가 발생할 것입니다.