본문 바로가기
Language/Golang

Golang (Go언어) 배열(Array)

by HeeWorld 2024. 11. 2.

Tucker의 Go 언어 프로그래밍 책과 유튜브를 통해 학습 중입니다.

 

Golang 마스코트 Gopher(고퍼)

배열

- 같은 타입의 데이터들로 이루어진 타입(Type)

- 배열을 이루는 각 값은 요소라고 하고, 요소(Element)를 가르키는 위치 값을 인덱스(Index)라고 함

- 배열의 인덱스는 0부터 시작하며 마지막은 '갯수 -1'

- 같은 타입의 여러 데이터를 하나의 변수로 효과적으로 사용할 수 있음

- 배열은 자료구조 중 한 종료임

var 변수명 [요소 개수]타입
package main

import "fmt"

func main() {
	var t [5]float64 = [5]float64{24.0, 25.9, 27.8, 26.9, 26.2}

	for i := 0; i < 5; i++ {
		fmt.Println(t[i])
	}
}

// 출력

24
25.9
27.8
26.9
26.2

 

var t [5]float64 = [5]float64{24.0, 25.9, 27.8, 26.9, 26.2}

- 앞[5]float64는 공간, 뒤 [5]float64는 값이며, =는 초기화, t라는 변수에 뒤에 변수를 복사하는 것

 

for i := 0; i < 5; i++ {

- 배열을 순회할 때 for문을 많이 사용함

 

→ 결과 값은 t[0], t[1], t[2], t[3], t[4]이 출력됨

→ 인덱스는 0부터 시작하기 때문에 t[0]이 첫 번째 값

배열

 

 

다양한 배열 변수 선언

var temps [5]float64 = [5]float64{24.3, 26.7}
temps[0] temps[1] temps[2] temps[3] temps[4]
24.3 26.7 0 0 0

 

- 5개를 갖는 배열이지만, 첫 번째와 두 번째는 지정한 값이 나머지 빈칸은 float64의 기본 값인 0.0으로 초기화 됨

 

var s =[5]int{1:10, 3:30}
s[0] s[1] s[2] s[3] s[4]
0 10 0 30 0

 

- s의 타입은 뒤의 값과 동일함

- 1:10 은 인덱스 1에 해당하는 자리에 10을 넣고, 3:30은 인덱스 3에 30을 넣는 것 = 특정 인덱스 값을 지정할 수 있음

 

x := [...]int{10, 20, 30}
x[0] x[1] x[2]
10 20 30

 

- [...]은 int 값(초기값)의 길이로 정하겠다는 의미로, 3개의 배열이 됨 = [3]int -> 길이가 늘지도 줄지도 않고 3개 고정

- 주의 할 점은 []만 사용해서 배열을 만들 경우 타입이 달라짐 -> 동적 배열(slice)로 길이가 늘어날 수 있는 배열임

 

 

배열 선언 시 개수는 항상 상수

- 배열 선언시 [n]int의 n은 항상 상수 값만 사용할 수 있음

- 변숫값을 배열 개수로 사용할 수 없음(변수의 값이 바뀌어 길이 값이 변할 수 있기 때문, 고정되어있어야 프로그램이 메모리를 얼마나 할당할지 알 수 있음)

- 상수를 길이로 사용할 수 있음(길이 값이 변하지 않음)

package main

const Y int = 3     // 상수

func main() {
	x := 5    // 변수
	a := [x]int{1, 2, 3, 4, 5}

	b := [Y]int{1, 2, 3}

	var c [10]int
}

// 결과

Error!!!

 

 

배열 순회

- Go뿐만 아니라 다른 언어에서도 아래처럼 사용함

package main

import "fmt"

func main() {
	nums := [...]int{10, 20, 30, 40, 50} // [5]int{10, 20, 30, 40, 50}과 동일

	nums[2] = 300

	for i := 0; i < len(nums); i++ {
		fmt.Println(nums[i])
	}
}

// 결과

10
20
300
40
50

 

- len() 내장함수로, 뒤에 배열이나 다른 값이 들어오는 경우 길이를 반환함

   → 위에 int의 값이 변해 배열 개수가 변할 수 있기 때문에 사용(편함)

 

range 순회

- Go에서 제공하는 방법

package main

import "fmt"

func main() {
	var t [5]float64 = [5]float64{24.0, 25.9, 27.8, 26.9, 26.2}

	for i, v := range t {
		fmt.Println(i, v)
	}
}

// 결과

0 24
1 25.9
2 27.8
3 26.9
4 26.2

 

- range는 키워드로 range 다음의 타입은 자료구조, 슬라이스, 스트링, 맵 등의 요소들을 돌아가면서 순회하면서 값을 2개를 반환함.

- 배열은 첫 번째 값에 인덱스(해당 하는 값의 인덱스 반환), 두 번째는 요소값을 출력하고 모든 값을 다 순회하면 for문이 끝남.

   → 만약 출력을 fmt.Println(v)만 하게 되면 Go에서는 컴파일 에러가 발생 = 변수를 선언하고 사용하지 않아서

   → 첫 번째 값을 사용하지 않고 싶은 경우 _(빈칸 지시자)을 사용하여 자리만 표시할 수 있음(인덱스 무효화)

 

 

배열은 연속된 메모리

- 배열은 연속된 메모리로, 배열 선언 시 컴퓨터는 연속된 메모리 공간을 확보함.

var a [10]int32

- 변수의 타입은 메모리 크기를 의미함 (Type데이터 형태도 의미하지만 크기도 의미함)

- [10]int32는 4bybe정수 요소로, 4byte가 10개 = 40byte 자리를 한 번에 (연속적으로)할당하여 각 요소 값을 채워 넣음

 

배열

- 컴퓨터는 배열의 시작주소는 (메모리 시작 주소) + (인덱스 * 타입 크기=요소)로 자리를 찾아감

- 배열은 요소를 찾아가는 속도가 매우 빠름

 

 

* 배열의 핵심

1️⃣ 배열은 연속된 메모리

2️⃣ 컴퓨터는 인덱스와 타입 크기를 사용해서 (간단하게)메모리 주소를 찾음

 

 

배열 복사

- 대입 연산자를 사용하면 배열 대 배열을 복사할 수 있음

package main

import "fmt"

func main() {
	a := [5]int{1, 2, 3, 4, 5}
	b := [5]int{500, 400, 300, 200, 100}

	for i, v := range a {
		fmt.Printf("a[%d] = %d\n", i, v)
	}

	fmt.Println()
	for i, v := range b {
		fmt.Printf("b[%d] = %d\n", i, v)
	}

	b = a

	fmt.Println()
	for i, v := range b {
		fmt.Printf("b[%d] = %d\n", i, v)
	}
}

// 결과 값

a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5

b[0] = 500
b[1] = 400
b[2] = 300
b[3] = 200
b[4] = 100

b[0] = 1
b[1] = 2
b[2] = 3
b[3] = 4
b[4] = 5

 

- =(대입연산자)가 실행되면 값을 복사하는데 우변의 값 타입의 크기만큼 복사함

- Golang 에서는 항상 공간의 크기가 같아야 함(양변의 크키가 다르면 복사할 수가 없음)

 

 

다중 배열

- 다중 배열은 중첩된 배열을 의미

- 이중, 삼중 배열처럼 여러 번 증첩해서 사용할 수 있음

- 이중 배열은 X, Y 좌표계의 위치 데이터들을 위해 주로 사용되어 이차원 배열이라고 부름

- 삼중 배열은 삼차원 공간상의 좌표 데이터들을 위해 주로 사용되어서 삼차원 배열이라고 부름

   → 컴퓨터는 차원 형태로 데이터를 저장하지 않고 차원에 상관없이 전체 요소 개수만큼 연속된 메모리 형태로 저장

 

var b [2][5]int

 

→ [5]int가 두 개인 배열로 b[0]은 첫 번째 [5]int 배열이 되고, b[1]은 두 번째 [5]int 배열이 됨

이중 배열

- 배열은 몇 개가 중첩되었던 [개수] 타입 형태

package main

import "fmt"

func main() {
	a := [2][5]int{            // 이중 배열 선언
		{1, 2, 3, 4, 5},
		{5, 6, 7, 8, 9},       // 여러 줄에 걸쳐 초기화할 때는 쉼표
	}
	for _, arr := range a {     // arr 값은 순서대로 a[0]의 배열 a[1]의 배열
		for _, v := range arr {  // v값은 순서대로 a[0]과 a[1] 배열의 각 요소
			fmt.Print(v, " ")    // v값 출력
		}
		fmt.Println()
	}
}

// 출력

1 2 3 4 5 
5 6 7 8 9

 

a := [2][5]int{  

→ [2][5]int로 이중 배열 a를 선언하고 초기화

→ a[0]은 첫 번째 [5]int 배열이고, a[1]은 두 번째 [5]int 배열이 됨 

 

{1, 2, 3, 4, 5},

→ a[0] 초기화용 {}


{5, 6, 7, 8, 9},

→ a[1] 초기화용 {}

→ Go에서는 중괄호가 열리고 닫힐 때 요소들이 같은 줄에서 닫히면 콤마가 필요 없지만 다른 줄에서 닫히면 ,(콤마)를 찍어줘야 함

 

for _, arr := range a {

→ arr은 5개짜리 int 타입의 배열

 

* 쉼표(,)

→ Go에서는 중괄호가 열리고 닫힐 때 요소들이 같은 줄에서 닫히면 콤마가 필요 없지만 다른 줄에서 닫히면 ,(콤마)를 찍어줘야 함

a := [2][5]int{ {1, 2, 3, 4, 5}, {5, 6, 7, 8, 9} } // 쉼표 없음

 

a := [2][5]int {
  {1, 2, 3, 4, 5},
  {5, 6, 7, 8, 9} }  // 쉼표 없음

→ 닫는 중괄호가 마지막 항목과 같은 줄에 있을 때 쉼표를 찍지 않음

 

a := [2][5]int {
   {1, 2, 3, 4, 5},  
   {5, 6, 7, 8, 9},  // 쉼표 찍어 줘야함
}

→ 닫는 중괄호가 마지막 항목과 같은 줄에 있지 않기 때문에 쉼표를 찍어야 함

 

a := [2][5]int {
   {1, 2, 3, 4, 5},  // 닫는 중괄호가 같은 줄에 있지 않아서 쉼표
   {5, 6, 7, 8, 9,  // 닫는 중괄호가 같은 줄에 있어서 쉼표 없음
   } }

 

 

배열 크기

- 배열이 선언되면 컴퓨터는 배열의 모든 요소를 저장할 수 있게 청크 형태로 연속적인 메모리가 할당 됨

- 배열 크기 = 타입 크기 * 항목 개수

- 배열을 이중, 삼중으로 선언하는 건 사람이 다루기 편하기 위함이지 컴퓨터 입장에서는 메모리 크기만 중요함

  → 컴퓨터 입장에서는 [3][2][5]int나 [30]int나 처리하는 방법은 동일함.

  → 단, 배열 내 항목을 접근할 때 [0][1][3]과 같이 여러 차수로 접근할 수 있느냐 [8]과 같이 하나의 차수로 접근하느냐의 차이

✓ [2][5]int의 이중 배열 크기는 [2]([5]int)라고 볼 수 있음 즉, [5]int 배열이 2개 있는 배열로 [5]int의 크기는 40바이트이니, [2][5]int 크기는 80(40 x 2)바이트가 됨. (int타입의 크기는 8바이트)