Powerful Concurrency Language Go
오랜만에 포스팅입니다! 그간 일이 너무 바빠서ㅜㅜ, 제일 중요한 자료구조 중 하나인 내장 자료구조 중 하나인! 배열에 대해서 학습해보도록 하겠습니다. 배열은 선형 자료구조의 대표적인 자료구조 중 하나입니다. 선형 자료 구조라 함은 ,
위와 같은 구조를 지닙니다. 선처럼 데이터들이 줄지어 적재되어있는 것을 볼 수 있는데, 이런게 선형 자료구조입니다. 그렇다면 고 언어에서는 어떻게 사용될까요? 일반적으로 C언어에서 배열을 선언한다고 하면 아래와 같이 선언할 수 있을 것입니다.
int arr[10000] = {1,2,3,4,5};
그런데 뭐 고 언어도 앞서 말씀드렸듯이, C언어의 영향을 많이 받은 언어인 만큼, C언어와 문법이 매우 흡사한 특징을 지닙니다. 그렇다면 고 언어에서의 배열은 어떻게 사용하는지 알아보도록 하겠습니다. 기본적인 배열 선언은 아래와 같습니다.
var arr[10]string
변수 선언에서는 당연히 var키워드를 사용해서 선언하게 되고, 끝에 배열 내부 데이터의 타입을 기재하면서 사용할 수 있습니다. 사용 예제는 아래와 같습니다.
package main
import "fmt"
func main() {
var arr[10]string
arr[0] = "H"
arr[1] = "e"
arr[2] = "l"
arr[3] = "l"
arr[4] = "l"
for foo, _ := range arr{
fmt.Println(arr[foo]);
}
bar := [5]int{1,2,3,4,5}
fmt.Println(bar)
}
위와같이 사용할 수 있습니다. 다른 언어나 마찬가지로 인덱스 접근을 통해서 값을 얻거나, 적재시킬 수 있으며, range키워드를 통해 배열의 인덱스에 차례대로 접근 또한 가능합니다.
Slice
고 언어의 배열 자료구조에서 중요한 개념 중 하나인 슬라이스에 대해서 알아보도록 하겠습니다. 우리가 위에서 배열을 선언할 때 배열의 크기를 주어서 선언 혹은 초기화한 것을 기억하시고 계실 겁니다. 그렇다는 것은 동적으로 크기가 할당하는 것 또한 가능하나, 어찌 되었든 고정된 크기를 가진다는 겁니다. 그렇다면 동적으로 크기를 할당하려면 어떻게 해야 할까요? 바로 슬라이스 개념을 사용하면 동적으로 사용 가능하며 고 언어에서는 일반적인 배열 선언보다 훨씬 널리 사용됩니다. 파이썬 언어를 해보신 분이라면, 혹은 다른 언어에서도 인덱싱 개념은 모두 있으나 제일 직관적인 파이썬에서 만약 슬라이스를 진행한다면 아래와 같이 사용 가능할 것입니다.
>>> foo = [1,2,3,4,5]
>>> foo[1:4]
[2, 3, 4]
아래와 같이 사용할 수 있겠죠. 그렇다고 고 언어는 특별히 다른 것 없이 기초적인 사용방법은 아주 똑같습니다. 아래 예제코드를 확인해보겠습니다.
package main
import "fmt"
func main() {
foo := [6]int{1,2,3,4,5}
var s []int = foo[1:4]
fmt.Println(s)
}
foo라는 배열을 선언함과 동시에 아래에서 새로운 s라는 배열에 foo라는 배열을 인덱싱 슬라이스를 통해 초기화해준 것을 확인할 수 있습니다. 또한 슬라이스는 배열의 내장 함수일 뿐, 데이터를 저장할 수 없습니다.. 배열을 다룰 때 사용하는 함수일 뿐 그 외 영역은 아니라는 점이 입니다. 하지만 슬라이스를 통해서 데이터를 수정하는 것은 가능합니다. 그렇게 된다는 것은,
foo := [4]string{
"Hello",
"World"
}
a := foo[1]
a[1] = "Good bye"
이것이 가능하다는 거겠죠. a라는 변수는 foo배열에서 [1]인덱스에 접근해 슬라이스를 해왔고, 아래에서 해당 인덱스의 데이터를 수정하는 것을 확인할 수 있습니다. 좀 더 나아가 보도록 하겠습니다.
슬라이스 된 배열은 고 언어에서 리터럴입니다. 그러니까 반복 가능하다는 뜻이며, 배열의 사이즈가 정적으로 할당되지 않으며, 기존의 배열에 참조되는 값으로 생성된다는 의미입니다. 조금 난해한 설명이기는 하나 아래 예제 코드를 보도록 하겠습니다.
package main
import "fmt"
func main() {
foo := []int{1,2,3,4}
fmt.Println(foo);
bar := []bool{true,false,true,false};
fmt.Println(bar);
zoo := []string{"he", "lo", "wo", "rl"}
fmt.Println(zoo)
qoo := []struct{
f int
b bool
z string
}{
{1, true, "he"},
{2, false, "lo"},
{3, true, "wo"},
{4, false, "rl"},
}
fmt.Println(qoo);
}
위와 같이 슬라이스 리터럴은 길이가 없으며, 동적으로 할당되는 배열인 거죠! 배열에서 슬라이스를 할 때 기본 값이 존재합니다. 뭐 우리가 배열을 선언할 때 아래와 같이 한다고 가정해본다면,
var foo[10] int
정수형 데이터를 담은 10개의 공간을 지닌 배열 맞나요? 그러니까 데이터가 총 10개가 들어갈 수 있다는 건데, 만약 이 배열을 fully 하게 담고 싶다면 아래와 같이 하면 되겠죠.
foo[0:10]
foo[:10]
foo[0:]
이렇게 하면 배열의 모든 데이터를 참조해서 슬라이스를 할 수 있게 됩니다. 물론 배열의 인덱스를 초과하는 기본값으로 슬라이스를 하게 되면 Index에러가 발생하니까 이 점은 유의해서 작성하기 바랍니다!
Slice length and Capacity
슬라이스에는 길이와 용량이라는 내장 함수가 각각 존재합니다. 만약 코드로 표현하자면 아래와 같습니다.
var foo[10]int
len(foo) // -> 슬라이스가 포함하는 요소의 개수 입니다. 그러니까
foo2 = foo[1:2] // -> 길이는 1이 반환되겠죠?
cap(foo) //-> 슬라이스의 첫 번째 요소부터 계산하는 배열 데이터의 개수입니다.
foo3 = foo[1:2] // -> 첫번째 요소부터 계산하기 때문에 인덱싱이 현재 1-2까지 되어있으므로
//5가 반환됩니다
만약 코드를 작성한다면, 아래와 같이 작성할 수 있습니다.
package main
import "fmt"
func lenandcap(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
func main() {
s := []int{1,2,3,4,5,6}
lenandcap(s)
s = s[:0]
lenandcap(s)
s = s[:4]
lenandcap(s)
s = s[1:2]
lenandcap(s)
}
또한 슬라이스의 초깃값은 0입니다. 그러니까 아래와 같이 선언된 배열이 있다고 가정해보겠습니다.
var foo[]int
그렇다면, 이 배열의 길이는 얼마일까요? 0입니다. 당연히 초깃값으로 지정된 값이 없기 때문에 0인거죠. 슬라이스를 만약에 진행한 경우에는 아래와 같이 nil 값이 반환되게 됩니다.
package main
import "fmt"
func main() {
var foo []int
fmt.Println(foo, len(foo), cap(foo))
if foo == nil {
fmt.Println("nil!")
}
}
당연히 0이니까 슬라이스 리터럴은 nil을 반환합니다! 그렇기 때문에 nil이 출력되는 거죠.
어후..
슬라이스의 기능을 적다보니 너무 많아서 다 담기가 힘드네요ㅠㅠㅠㅠ 슬라이스 2편은 이후에 또 작성해보도록 하겠습니다!
댓글로 피드백, 문의, 질문 모두 환영합니다!!
감사합니다😆😆😆😆
'Go' 카테고리의 다른 글
[Go#6] Go언어 pointer, struct 문법!!! (0) | 2021.01.14 |
---|---|
[Go#5] Go언어 Switch, Defer 문법!!! (0) | 2021.01.12 |
[Go#4] Go언어 For, if_(반복문, 제어문) 이해하기!!! (0) | 2021.01.07 |
[Go#3] Go언어 패키지, import-export, function 이해하기!!! (0) | 2021.01.05 |
[Go#2] Go언어 변수_ 상수, 타입, 타입 캐스팅!!! (0) | 2021.01.03 |
댓글