본문으로 건너뛰기

배열(Array)

배열이란?

Go의 배열은 동일한 타입의 값을 고정된 개수만큼 연속된 메모리에 저장 하는 자료구조입니다. 선언 시 길이가 타입의 일부로 포함되기 때문에 [3]int[5]int는 서로 다른 타입입니다.

대부분의 언어에서 배열은 참조 타입이지만, Go에서 배열은 값 타입(value type) 입니다. 배열을 함수에 전달하거나 다른 변수에 대입하면 전체가 복사됩니다.

package main

import "fmt"

func main() {
// 배열 선언 — 선언 즉시 제로값으로 초기화
var a [3]int // [0 0 0]
a[0] = 10
a[1] = 20
a[2] = 30
fmt.Println(a) // [10 20 30]

// 배열 리터럴
b := [3]int{10, 20, 30}
fmt.Println(b) // [10 20 30]

// 길이를 컴파일러가 자동으로 계산 (... 사용)
c := [...]string{"Go", "Python", "Rust"}
fmt.Println(len(c)) // 3

// 인덱스를 지정한 부분 초기화
d := [5]int{1: 100, 3: 300} // [0 100 0 300 0]
fmt.Println(d)
}

배열의 값 타입 특성

배열이 값 타입이라는 점은 슬라이스와 가장 큰 차이입니다.

package main

import "fmt"

func modifyArray(arr [3]int) {
arr[0] = 999 // 복사본을 수정 — 원본에 영향 없음
}

func modifyArrayPtr(arr *[3]int) {
arr[0] = 999 // 포인터로 전달 — 원본 수정
}

func main() {
original := [3]int{1, 2, 3}

// 값 복사
copied := original
copied[0] = 100
fmt.Println(original) // [1 2 3] — 영향 없음
fmt.Println(copied) // [100 2 3]

// 함수에 값으로 전달
modifyArray(original)
fmt.Println(original) // [1 2 3] — 변경 없음

// 포인터로 전달
modifyArrayPtr(&original)
fmt.Println(original) // [999 2 3] — 변경됨
}

배열 비교

Go 배열은 ==!= 연산자로 직접 비교할 수 있습니다. 길이와 타입이 같아야 하고, 모든 요소가 동일하면 같은 배열입니다.

package main

import "fmt"

func main() {
a := [3]int{1, 2, 3}
b := [3]int{1, 2, 3}
c := [3]int{1, 2, 4}

fmt.Println(a == b) // true
fmt.Println(a == c) // false
fmt.Println(a != c) // true

// 컴파일 에러: 길이가 다른 배열은 비교 불가
// d := [4]int{1, 2, 3, 4}
// fmt.Println(a == d) // invalid operation
}

배열 순회

for 루프와 range를 사용해 배열을 순회합니다.

package main

import "fmt"

func main() {
scores := [5]int{90, 85, 78, 92, 88}

// 인덱스와 값 모두 사용
for i, v := range scores {
fmt.Printf("scores[%d] = %d\n", i, v)
}

// 인덱스만 필요할 때
for i := range scores {
fmt.Println(i)
}

// 값만 필요할 때 (인덱스 무시)
sum := 0
for _, v := range scores {
sum += v
}
fmt.Printf("합계: %d, 평균: %.1f\n", sum, float64(sum)/float64(len(scores)))
}

다차원 배열

2차원 이상의 배열을 선언할 수 있습니다. 행렬 연산, 게임 보드 등에 활용됩니다.

package main

import "fmt"

func main() {
// 2차원 배열 선언
var matrix [3][3]int
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
matrix[i][j] = i*3 + j + 1
}
}
// [[1 2 3] [4 5 6] [7 8 9]]
fmt.Println(matrix)

// 2차원 배열 리터럴
grid := [3][3]string{
{"X", "O", "X"},
{"O", "X", "O"},
{"X", "O", "X"},
}
for _, row := range grid {
fmt.Println(row)
}

// 3차원 배열 (RGB 이미지 픽셀 표현 예시)
var rgb [2][2][3]uint8
rgb[0][0] = [3]uint8{255, 0, 0} // 빨강
rgb[0][1] = [3]uint8{0, 255, 0} // 초록
rgb[1][0] = [3]uint8{0, 0, 255} // 파랑
rgb[1][1] = [3]uint8{255, 255, 0} // 노랑
fmt.Println(rgb)
}

실전 예제 — 행렬 곱셈

package main

import "fmt"

// 3x3 행렬 곱셈
func matMul(a, b [3][3]int) [3][3]int {
var result [3][3]int
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
for k := 0; k < 3; k++ {
result[i][j] += a[i][k] * b[k][j]
}
}
}
return result
}

func printMatrix(m [3][3]int) {
for _, row := range m {
fmt.Println(row)
}
}

func main() {
a := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
b := [3][3]int{
{9, 8, 7},
{6, 5, 4},
{3, 2, 1},
}

fmt.Println("A × B =")
printMatrix(matMul(a, b))
// [30 24 18]
// [84 69 54]
// [138 114 90]
}

배열 vs 슬라이스 — 언제 배열을 쓸까?

항목배열슬라이스
크기고정 (컴파일 타임)가변 (런타임)
타입크기 포함 ([3]int)크기 미포함 ([]int)
복사전체 복사헤더만 복사 (내부 배열 공유)
제로값요소 제로값nil
비교== 가능불가 (reflect.DeepEqual 사용)
주 용도크기가 확정된 데이터일반적인 동적 컬렉션

배열이 적합한 경우:

  • SHA-256 해시처럼 크기가 고정된 값 ([32]byte)
  • 행렬·벡터 연산
  • 배열 자체를 맵의 키로 사용할 때 (슬라이스는 맵 키 불가)
// 배열을 맵 키로 사용 — 슬라이스는 불가
cache := map[[2]int]int{}
cache[[2]int{1, 2}] = 100
cache[[2]int{3, 4}] = 200
fmt.Println(cache) // map[[1 2]:100 [3 4]:200]