안녕하세요, 주니어 개발자 Eon입니다.
이번 포스트는 배열에 관한 내용입니다.
배열은 같은 타입을 가지는 변수들의 묶음입니다.
또, 선언된 변수들은 각각 연속된 메모리 공간에 할당됩니다.
배열은 수학의 행렬과 비슷하다고 볼 수 있습니다.
행과 열의 크기를 정하고, 그 안에 값을 저장합니다.
수학에서의 행렬
1 × 2 행렬
2 × 2 행렬
프로그래밍에서의 배열
1 × 2 의 형태는 1차원 배열
var a [2]int = [2]int{1, 2}
2 × 2 의 형태는 2차원 배열
var a [2][2]int = [2][2]int{{1, 2}, {3, 4}}
배열은 인덱스
와 요소
로 구성돼 있습니다.
인덱스
: 배열에 들어가 있는 각 요소들의 순서이며, 0부터 시작함
요소
: 각 인덱스가 가지는 값
배열
a
는int
타입의요소
4개
로 이루어졌습니다.
각요소
를인덱스
순서에 맞게{1, 2, 3, 4}
로 초기화합니다.
각인덱스
의요소
는a[인덱스] = 요소
로 나타낼 수 있습니다.
var a int
위와 같이 int
타입의 변수 a
를 선언했습니다.
여러 개의 변수를 선언하기 위해서는 아래와 같이 수행했습니다.
var a, b int
var a [5]int
위에서 int
타입의 변수 5개
를 a
라는 배열에 선언했습니다.
배열 a
는 5개
의 int
타입의 요소
를 가집니다.
배열은 초기에 지정한 크기를 변경할 수 없습니다.
위의 예시에서인덱스
가5개
인 배열을 선언했는데, 이 배열의 크기는 4로 줄어들 수도, 6으로 늘어날 수도 없습니다.
때문에, 변수를 배열의 크기를 지정할 때 사용할 수 없고 상수는 사용이 가능합니다.
배열을 선언하는 방법은 위에서 소개한 방법 외에도 몇 가지 더 있습니다.
위에서 소개한 방법을 포함해서 작성하겠습니다.
var a [5]int
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
위에서 소개한 방법입니다.
먼저 배열을 선언함과 동시에 각 요소
에 개별적으로 접근하여 값을 지정했습니다.
값을 지정하지 않으면, 초기화를 하지 않았기에
a
안의 모든요소
의 값은int
타입의 zero-value인0
입니다.
var a [5]int = [5]int{1, 2, 3, 4, 5}
참고 : golang go-static에서는 위와 같이 선언과 초기화를 같은 줄에 작성하는 걸 권장합니다.
var a [5]int = [5]int{1, 2, 3, 4}
인덱스
가 5개
인 배열에 값을 4개
만 지정하는 경우, 초기화하지 않은 인덱스
의 값은 zero-value가 됩니다.
fmt.Println(a[4])
// 0
b := [3]bool{true, false, true}
위와 같이 짧은 선언과 초기화를 동시에 할 수 있습니다.
var c = [5]float64{1: 1.1, 3: 3.3}
var 배열이름 = [사이즈]타입{인덱스 : 요소, 인덱스 : 요소, ...}
fmt.Println(c)
// [0 1.1 0 3.3 0]
d := [...]string{"one", "two", "three"}
fmt.Println(len(d))
// 3
len() 은 길이(length)를 반환하는 기본 내장함수입니다.
위에서 len()은 배열 d의 길이를 반환합니다.
var mul [2][3]int = [2][3]int{{1, 2, 3}, {4, 5, 6}}
var mul [2][3]int = [2][3]int{
{1, 2, 3},
{4, 5, 6},
}
이렇게 여러 줄로 초기화할 수 있습니다.
단, 새로 개행을 하고 괄호를 닫을 경우, 마지막 원소의 끝에 콤마(,)를 붙여야 합니다.
가장 쉬운 예시로, 차원을 표현할 때 사용할 수 있습니다.
이미지는 2차원입니다.
x, y축으로 이미지를 표현할 수 있습니다.
각 픽셀의 위치를 좌표로 하고, 그 위치에 들어가는 값으로 색을 정할 수 있습니다.
3D 물체는 3차원입니다.
x, y, z축으로 표현할 수 있습니다.
이미지와 마찬가지로 모든 좌표에 대하여 값을 넣음으로써 물체를 표현할 수 있습니다.
배열의 각 요소에 순차적으로 접근할 수가 있습니다.
앞서 반복문에 대한 포스트에서도 짧게 소개했습니다.
먼저 아래 배열 순회 예시에서 사용되는 배열 하나를 아래와 같이 선언하겠습니다.
var a [5]int = [5]int{1, 2, 3, 4, 5}
a[2] = 10
for i := 0; i < len(a); i++ {
fmt.Printf("%d ", a[i])
}
// 1 2 10 4 5
배열[인덱스]
로 원하는 인덱스 요소를 사용할 수 있습니다.
위와 같이 특정 인덱스의 값을 바꿀 수도 있습니다.
i := 0
for i < len(a) {
fmt.Printf("%d ", a[i])
}
// 1 2 3 4 5
i := 0
for {
if i >= len(a) {
break
}
fmt.Printf("%d ", a[i])
i++
}
// 1 2 3 4 5
for i, v := range a {
fmt.Println("index : %d & value : %d", i, v)
}
/* The result of output
index : 0 & value : 0
index : 1 & value : 0
index : 2 & value : 0
index : 3 & value : 0
index : 4 & value : 0
*/
for i, v := range a {...}
:i (index), v (value)
가 들어갑니다.
이 루프 안에서는a[i]
를 사용하고v
를 사용하지 않아도 됩니다.
golang에서는 선언하고 사용하지 않는 변수가 있으면 컴파일 에러를 일으키기 때문에,a[i]
로 사용하려면 아래와 같이v
를 선언하지 않으면 됩니다.
for i := range a {...}
배열을 선언하면 다른 변수와 마찬가지로 메모리에 공간이 할당됩니다.
아래 그림은 배열이 아닌 변수와 배열의 메모리 할당을 나타냅니다.
변수는 할당되면 그 변수 타입의 크기만큼 메모리 공간이 할당됩니다.
배열도 마찬가지입니다.
다만 배열은 맨 처음 인덱스를 기준으로 하여, 이어지는 인덱스는 전부 연속적인 메모리 공간에 할당됩니다.
위의 그림은 int32
타입의 배열이므로, 각 인덱스의 주소값은 int32
의 크기만큼의 차이를 둡니다.
0번 인덱스의 주소값이 100
이라고 할 때, 1번 인덱스의 주소값은 100 + sizeOf(int32)
가 됩니다.
(golang에서 sizeOf는 unsafe 패키지 내에 Sizeof() 함수로 사용할 수 있습니다. 다른 포스트에서 설명하겠습니다.)
32bit = 4byte 이므로, 각 인덱스의 주소값은 4바이트 단위로 증가합니다.
주소값 : 메모리 공간의 주소 (할당된 공간의 위치를 나타냄)
golang에서의 다차원 배열은, 메모리 영역 할당이 연속적입니다.
위와 같이 서로 떨어져 있는 메모리 영역에 공간을 할당받는 것이 아니라,
아래와 같이 연속된 메모리 영역에 공간을 할당받습니다.
값의 복사는 쉽게 할 수 있습니다.
var a, b int = 2, 3
a = b
fmt.Println(a)
// 3
위와 같이 변수에 값을 복사하는 것은 '대입'이라는 개념을 통해서 이해할 수 있습니다.
단, golang은 최강타입 언어라고 했습니다.
타입이나 크기가 다르면 값의 복사를 불허합니다.
var a int = 2
var b int64 = 3
a = b
위 과정은 에러가 발생합니다. 64비트 운영체제에서 사용해도 말입니다.
int는 64비트 운영체제에서 int64와 사이즈가 동일합니다.
하지만 타입명이 다릅니다. 그래서 에러가 발생합니다.
var a [5]int = [5]int{1, 2, 3, 4, 5}
var b [5]int = [5]int{6, 7, 8, 9, 10}
a = b
fmt.Println(a)
// [6 7 8 9 10]
위의 경우엔 정상적으로 복사된 배열을 출력합니다.
배열의 타입과 크기가 모두 동일해서 가능했습니다.
여기서 타입이나 크기를 서로 다르게 지정을 한다면 복사가 불가능합니다.
이번 포스트는 배열에 대한 내용이었습니다.
감사합니다.👍