Array(배열)과 slice(슬라이스)
Array(배열)
동일한 자료형의 1개 이상의 값들을 저장하는 타입으로 연속된 메모리에 데이터가 저장됩니다. 다음과 같이 정의 합니다.
var 변수명[요소개수]타입 예) var a[5]int
위 예는 정수형 데이터 5개를 포함하는 배열 a를 선언한 것입니다. 그러나 초기값이 할당되지 않았으므로 기본값이 0이 됩니다.
func main() {
var a [5]int
fmt.Println(a)
}
[0 0 0 0 0]
배열의 크기는 상수(const)만 가능합니다.
var len int = 5
func main() {
var a [len]int
fmt.Println(a)
}
invalid array length len
const len = 5
func main() {
var a [len]int
fmt.Println(a)
}
[0 0 0 0 0]
초기값은 다음과 같이 선언합니다.
a := [5]int{1,2,3,4,5}
b := [3]string{"A", "B", "C"}
지정된 요소에만 초기값을 지정할 수 있습니다. 배열의 인덱스(요소의 위치)는 0부터 시작하는 양의 정수입니다.
a := [5]int{1: 10, 4: 3}
[0 10 0 0 3]
인덱스를 사용하여 배열의 특정한 요소를 호출 또는 치환 등 조정할 수 있습니다.
import "fmt"
func main() {
a := [5]int{10, 20, 30, 40, 50}
fmt.Println(a)
fmt.Println(a[0])
fmt.Println(a[4])
a[4] = 5
fmt.Println(a)
fmt.Println(a[2:4])
}
[10 20 30 40 50] 10 50 [10 20 30 40 5] [30 40]
배열의 길이에 가변인자를 적용하면 초기값들의 수만큼을 포함하는 배열이 생성됩니다. 즉, 초기값의 수가 배열의 메모리 크기를 지정하는 것입니다. 그러므로 초기값의 수를 지정하지 않을 경우는 에러가 발생합니다.
var b [...]int
error!
길이에 대한 정보를 전달하지 않은 경우에도 초기값의 수만큼의 결과가 생성되는데 이 객체는 배열이 아니고 슬라이스(slice)입니다.
func main() {
a := [...]int{1, 2, 3, 4, 5, 6}
fmt.Printf("%T\n ", a)
fmt.Println(a)
b := []int{2, 6, 9} //slice
fmt.Printf("%T\n", b)
fmt.Println(b)
}
[6]int [1 2 3 4 5 6] []int [2 6 9]
배열 복사
배열은 연속된 메모리에 저장된 형태입니다. 그러므로 배열을 복사하기 위해서는 같은 메모리 크기를 가져야 합니다. 두 배열의 복사를 위한 a=b에서 a와 b는 같은 배열의 크기를 가져야 됨을 의미합니다.
import "fmt"
func main() {
a := [...]int{1, 2, 3, 4, 5}
var b [5]int
fmt.Println(a)
fmt.Println(b)
b = a
fmt.Println(a)
fmt.Println(b)
}
[1 2 3 4 5] [0 0 0 0 0] [1 2 3 4 5] [1 2 3 4 5]
다차원 배열
배열의 크기를 지정하기 위한 []는 1개의 차원을 의미합니다. 즉, []의 수가 차원을 결정합니다. 다음은 (2, 5)의 형태를 가지고 모두 정수인 2차원 배열을 생성하기 위한 코드입니다.
var a[2][5]int
다음 코드들은 위의 2차원 배열에 초기값을 설정하고 for문을 적용하여 각각을 출력하기 위한 것입니다. 배열은 연속된 메모리 형태로 첫 반복은 배열 내의 배열이 되고 두번째 반복에서 각 배열의 요소들을 반복해서 나타냅니다.
import "fmt"
func main() {
a := [2][5]int{
{1, 2, 3, 4, 5},
{10, 20, 30, 40, 50},
}
fmt.Println(a)
fmt.Println()
for _, arr1 := range a {
fmt.Println(arr1)
fmt.Println()
}
for _, arr1 := range a {
for _, arr2 := range arr1 {
fmt.Println(arr2)
}
fmt.Println()
}
[[1 2 3 4 5] [10 20 30 40 50]] [1 2 3 4 5] [10 20 30 40 50] 1 2 3 4 5 10 20 30 40 50
slice(슬라이스)
동적배열 즉, 배열 길이의 확장과 축소가 가능한 배열입니다. 배열의 선언에서 길이를 지정하지 않은 상태로 정의합니다. 초기값의 할당은 배열과 같습니다. 배열과 같이 []의 갯수로 다차원 슬라이스(동적 다차원 배열)를 생성합니다.
var 변수명 []타입
var a []int = []int{1,2,3,4,5}
var a = []int{1,2,3,4,5}
a :=[]int{}
a := []int{1,2,3,4,5}
var 변수명 [][]타입 //2차원 슬라이스
b := [][]int {{}}
b := [][]int{{1,2,3}{4,5,6,7,8}}
초기값 할당은 요소의 수정은 배열과 같이 인덱스를 사용할 수 있으며 다차원의 경우도 동일합니다. 특히, 특정 인덱스 요소만을 초기화할 경우 제일 큰 인덱스+1이 슬라이스의 크기가 되며 지정되지 않은 요소는 기본값이 할당됩니다(int의 기본값은 0)>
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5}
fmt.Println(a)
for i := 0; i < 5; i++ {
a[i] = i * 10
}
fmt.Println(a)
b := []int{0: 3, 2: 5, 7: 10}
fmt.Println(b)
c := [][]int{{0: 10, 3: 2}, {1: 3, 5: 9}}
fmt.Println(c)
}
[1 2 3 4 5] [0 10 20 30 40] [3 0 5 0 0 0 0 10] [[10 0 0 2] [0 3 0 0 0 9]]
슬라이스는 배열의 조각을 나타낼 수 있습니다. 이 경우 슬라이스는 배열을 참조하는 것으로 슬라이스의 요소를 수정하면 원 배열의 동일한 값을 수정하는 것과 같습니다. 즉, 파이썬의 얕은복사(shallow copy)와 같습니다.
import "fmt"
func main() {
names := [4]string{"철수", "양희", "영재", "지수"}
fmt.Println(names)
a := names[0:2]
b := names[1:3]
fmt.Println(a, b)
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
}
[철수 양희 영재 지수] [철수 양희] [양희 영재] [철수 XXX] [XXX 영재] [철수 XXX 영재 지수]
슬라이싱의 기본값, low bound:0, high bound: 배열의 길이
import "fmt"
func main() {
s := []int{2, 1, 5, 6, 10, 9}
fmt.Println(s)
s = s[:]
fmt.Println(s)
s = s[3:5]
fmt.Println(s)
}
[2 1 5 6 10 9] [2 1 5 6 10 9] [6 10]
배열과 슬라이스는 동일한 자료형의 값들의 묶음입니다. 다른 자료형의 배열(슬라이스)를 그룹화하기 위해 구조체(struct)를 자료형으로 하는 배열을 생성할 수 있습니다.
import "fmt"
func main() {
id := []int{2, 3, 7, 11, 13}
eval := []bool{true, false, true, true, false, true}
fmt.Println(id, eval)
s := []struct {
j int
b bool
}{
{id[0], eval[0]},
{id[1], eval[1]},
{id[2], eval[2]},
{id[3], eval[3]},
{id[4], eval[4]},
}
fmt.Println(s)
}
[2 3 7 11 13] [true false true true false true]
[{2 true} {3 false} {7 true} {11 true} {13 false}]
슬라이스 길이와 용량
슬라이스에는 길이와 용량이 모두 있습니다.
- 슬라이스의 길이: 슬라이스에 포함된 요소의 수
- 슬라이스의 용량: 슬라이스의 첫 번째 요소부터 세어 기본 배열의 요소 수
- 슬라이스 s의 길이와 용량은 표현식 len(s) 및 cap(s)를 사용하여 얻을 수 있습니다.
충분한 용량이 있는 경우 슬라이스를 다시 슬라이스하여 길이를 확장할 수 있습니다. 예제 프로그램에서 슬라이스 작업 중 하나를 변경하여 용량을 초과하여 확장해 보고 무슨 일이 일어나는지 살펴보세요.
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
func main() {
s := []int{2, 1, 5, 6, 10, 9}
printSlice(s)
s = s[:0]
printSlice(s)
s = s[3:5]
printSlice(s)
}
len=6 cap=6 [2 1 5 6 10 9] len=0 cap=6 [] len=2 cap=3 [6 10]
Nil 슬라이스
nil은 명시적인 초기값을 할당하지 않고 변수를 만들었을 때 해당변수가 갖게 되는 값입니다. 그러므로 nil 슬라이스의 길이와 용량이 0이며 기본 배열이 없습니다.
func main() {
var s []int
fmt.Println(s, len(s), cap(s))
if s == nil {
fmt.Println("nil!")
}
}
[] 0 0 nil!
댓글
댓글 쓰기