Go에서 가장 많이 사용하는 방법으로 동적배열을 말합니다.
선언하는 방법은 어렵지 않습니다.
하지만 그렇다고 완전한 동적배열을 의미하는것은 아니고 ArrayList
라고 생각을 하면 됩니다.
func main() {
var 변수명 []int
if len(변수명) == 0 {
fmt.Println("slice의 길이는 0 입니다.")
}
변수명[1] = 10
fmt.Println(변수명)
}
이 코드를 실행시키면 에러가 나게 됩니다.
왜냐하면 ArrayList
의 형태를 띄고 있기 떄문에 0에 해당하는 값이 없이 바로 1에는 집어넣을수 없기 떄문입니다.
ArrayList
는 이전값이 현재값을 가르키는 방식으로 작동을 합니다.초기화를 하는 방법은 내장함수를 사용하는 방법과 하드코딩 하는 방법이 있습니다.
a := make([]int,3)
- [0,0,0]과 같습니다.
a := []int{0,0,0}
- 위 코드와 같은 결과를 내뱉습니다.
js의 push와 같은 역할을 하는 append
slice := []int{1,2,3}
slice2 := append(slice, 4)
fmt.Println(slice)
fmt.Println(slice2)
js의 push내장함수와 같은 역할을 하는 내장함수 입니다.
기존의 변수는 그대로 유지시키면서 완전히 새로운 변수를 생성해 냅니다.
만약 기존 변수를 변화시키고 싶으면
slice = append(slice,4)
이렇게 적어주면 됩니다.append(slice,4,5,6,7)
새로운 변수를 만드는 행위는 메모리를 잡아먹기 떄문에 주의해야 합니다.
슬라이스의 동작원리
앞서 적은 내용은 굉장히 쉽고 다가가기에 어렵지 않습니다.
하지만 내부에서 작동하는 구조를 자세히 살펴보아야 후에 발생하는 문제를 해결할수 있습니다.
type SliceHeader struct {
Data unitptr // 실제 배열을 가르키는 값
Len int // 배열의 길이
Cap int // capacity : 용량을 의미
}
예를들어보면
data는 값을 의미하기 떄문에 어렵지 않습니다.
Len값은 배열의 길으를 의미하기 떄문에 []int{1,2,3,4,5}
와 같은 구문에서 5
를 의미합니다.
Cap는 용량을 의미하며 Len과 비슷하지만 Len은 실제로 사용되는 부분
을 의미하고 Cap는 최대 사용할수 있는 값으로 인식할수 있습니다.
그러기 떄문에 좀더 정확하게 말을 하자면
슬라이스는 Go에서 제공하는 배열을 가리키는 포인터 입니다.
좀더 Cap
와 len
의 차이점을 알아보면
slice := make([]int, 3, 5)
라는 코드를 살펴 보면 차이점을 명확하게 알수 있습니다.
앞서 배운것처럼 make
라는 내장함수는 초기화를 시켜주는 함수이며 이처럼 인자값을 3개까지 넣을수 있습니다.
이떄 3은 Len
을 의미하고, 5는 Cap
를 의미합니다.
[0][0][0][][]
이 만들어 지게 될 것이고Len
값은 = 3, Cap
값은 = 5라는 것을 알수 있습니다.array와 slice의 차이
func changeArray(array2 [5]int){
array2[2] = 200
}
func changeSlice(slice2 []int){
slice2[2] = 200
}
func main(){
array := [...]int{1,2,3,4,5}
slice := []int{1,2,3,4,5}
changeArray(array)
changeSlice(slice)
fmt.Println(array)
fmt.Println(slice)
}
이 결과를 보면 array는 바뀌지 않고 slice는 바뀌게 됩니다.
앞서 한번 다루었던 것처럼 array과 array2는 다룬 주소값을 가지게 되기 떄문에 변화가 없는 것 입니다.
반면에 slice같은 경우에는 구조상 가르키고 있는 데이터값이 있기 떄문에 함수로 들어가도 똑같은 주소값을 가르키게 됩니다.
append()의 동작원리
생각보다 심오하고 어려운 내용이였습니다.
간단하게 설명하면 깊은복사와 얕은복사에 대한 내용이 될 것 같습니다.
slice := make([]int, 3, 5)
- 이렇게 만들어 Len = 3 , Cap = 5인 slice가 만들어 집니다.
slice2 := append(slice, 4,5)
- 이후 append를 적용시키면 [0][0][0][4][5]라는 값이 만들어 집니다.
이떄 slice[1] = 100을 적용하면 어떻게 될까요
결과는 : slice, slice2모두 바뀌게 됩니다.
왜냐하면 같은 주소값을 가지게 되기 때문입니다.
다른 주소값을 가지게 되는 경우는 Cap와 Len이 동일한 값을 가질 떄 입니다.
slice := make([]int, 5, 5)
를 만들어 주면 cap와 len이 같은 값을 가지고 있는 상태가 되기 떄문에 만약
slice2 := append(slice, 4,5)
를 적용하면 새로운 값을 만들어서 그곳에 값을 복사후 값을 삽입하는 작업이 진행이 됩니다.
그러기 떄문에 다른 주소값을 가진 slice가 만들어 지고
slice[1] = 200 의 결과로는 slice만 변하게 됩니다.