0517-Go(2)

망지·2022년 5월 17일
0
post-custom-banner

기본 데이터 타입과 선언


사용하지 않는 변수

  • 지역 변수는 반드시 사용되어야 함
    하지만,
func main() {

	x := 20
	fmt.Println(x)

	x = 30

}
D:\go>go run hello.go
20

=> 마지막 x를 재정의 하고 사용하지 않았으나 ( 비효율적 코드) 잡아내지 못함.
(golangci-lint 는 가능)

  • 사용되지 않는 상수(const)는 허용

변수와 상수 이름 짓기

  • Go는 유니코드 문자를 식별자로 허용

    • 권장하지 않음
  • Go는 관용적으로 스네이크 표기법 사용하지 않음

    • 낙타 표기법, 파스칼 표기법 사용
  • 상수: 모든 문자를 대문자, 각 단어는 밑줄로 구분(보통 다른 언어들)

    • Go는 이러한 일반 상수 이름 규칙을 따르지 않는다.
    • 패키지에 포함된 요소를 외부에 노출시킬지(expose) 여부를 식별자의 첫글자가 대소문자 여부로 결정
  • 함수 내에서는 짧은 변수 이름 선호

    • 변수의 의미보다 간결성을 선호하는 Go
  • 짧은 이름

    • 코드 간결
    • 코드의 복잡도 판별 기준
int iScore = 10
String strName

약타입 언어들..(변수 이름에 type포함) Go는 X (Go는 강타입 언어.) => 더 짧은 변수 이름 사용하자.

  • 패키지 블록 내
    • 설명적인 변수, 상수 이름 사용하자

복합 타입


1. 배열(array)

  • 같은 타입의 여러 데이터를 담음
  • 시퀀스 타입 (순서 중요)
  • Go에서는 선호하지 않음(특별한 경우에만 사용)
  • 선언 시 크기를 지정해야 함
  • 크기를 변경할 수 없다
  • 배열 간 비교 가능(==, !=)
  • 배열 인덱스는 범위를 벗어나서는 안되고 음수도 불가

- 선언방법

1) 제로 값으로 선언: 배열의 크기와 배열 내 요소 타입 지정

package main

import "fmt"

func main() {

	var x [3]int
	fmt.Println(x)
}
D:\go>go run hello2.go 
[0 0 0]

2) 배열 초깃값 지정: 배열 리터럴 사용

func main() {
	var x [3]int               // 데이터 타입
	var y = [3]int{10, 20, 30} // array literal

	fmt.Println(x)
	fmt.Println(y)
	fmt.Println([2]float32{2.1, 3.3})
}

D:\go>go run hello2.go 
[0 0 0]
[10 20 30]
[2.1 3.3]
 

3) 희소 배열: 대부분의 요소가 0인 배열


func main() {
	var x [3]int               // 데이터 타입
	var y = [3]int{10, 20, 30} // array literal
	var z = [12]int{1, 5: 4, 6, 10: 100, 15} //n:3 n번쨰에 3

	fmt.Println(x)
	fmt.Println(y)
	fmt.Println(z)
}
D:\go>go run hello2.go
[0 0 0]
[10 20 30]
[2.1 3.3]
[1 0 0 0 0 4 6 0 0 0 100 15]

4) 배열 크기 지정하지 않고: 배열 리터럴 필요
5) 다차원 배열: []의 개수가 차원 수


  • 배열 요소 접근
    • [] 사용
  • 배열 길이
    • len() 함수
  • Go에서 배열을 잘 사용하지 않는 이유
    • 배열의 크기가 배열의 타입을 결정하는데 사용되기 때문
    • 즉, 원소의 타입이 같은 배열이라도 크기가 다르면 다른 타입
    • 크기(길이)가 다르면 타입 변환도 불가능

2. 슬라이스

  • 일련의 값(시퀀스)을 저장하는 자료 구조
  • 순서 중요
  • 슬라이스의 크기는 타입의 일부가 아니다 (배열과의 큰 차이점) 10개 들어있는 슬라이스를 받으나 0개 들어있는 슬라이스를 받으나 똑같다. 파이썬의 리스트와 유사. 차이점은 타입을 지정해주어야 함.
  • 슬라이스 간 비교는 불가 (배열과 다른 점)
  • 단, nil과는 비교 가능(==, !=)

  • 슬라이스 선언 (슬라이스 크기를 지정하지 않음)
    1) 슬라이스 초깃값 지정: 슬라이스 리터럴
func main() {
	var x [3]int               // 데이터 타입
	var y = [3]int{10, 20, 30} // array literal
	var q = []int{10, 20, 30}  //슬라이스

	fmt.Println(x)
	fmt.Println(y)
	fmt.Println(q)
}
D:\go>go run hello2.go
[0 0 0]
[10 20 30]
[10 20 30]

2) 희소 슬라이스
희소 배열과 유사.
3) 다차원 슬라이스

	var r = [][]int{{1, 2}, {4, 5, 6}}

4) 제로 슬라이스: 슬라이스 리터럴 없이 선언만 하는 것
- 슬라이스의 제로 값은 nil
- nil : 값의 부재(absence of value) 상태
- nil은 타입이 없음
5) 비어있는 슬라이스: 슬라이스 리터럴에 초깃값이 없는

  • 슬라이스의 길이
    • len() 함수 사용

2.1. append

  • 슬라이스에 새로운 요소 추가
func main() {
	var x []int // zero slice

	x = append(x, 10)

	fmt.Println(x)

}
D:\go>go run hello2.go
[10]
func main() {
	var x = []int{3, 4, 5}

	x = append(x, 10)

	fmt.Println(x)

}
D:\go>go run hello2.go
[3 4 5 10]
  • append의 결과를 할당하지 않으면 컴파일 에러

2.2. 수용력(capacity)

  • 예약된(미리 준비된) 연속적인(consecutive) 메모리 공간의 크기
  • 길이와 수용력
  • 수용력 >= 길이
  • 요소가 추가되면 길이는 커지고 결국 수용력과 같아짐
  • 길이과 수용력이 같은 상태에서 요소가 추가되면 Go 런타임이 더 큰 수용력을 가지는 새로운 슬라이스를 할당, 원본 슬라이스의 값들은 새 슬라이스에 복사됨, 추가된 값은 새 슬라이스에 append되고 이 새 슬라이스가 반환된다.
  • 수용력이 1024 보다 작으면 2배 증가, 그렇지 않으면 25%씩 확장
  • cap() vs. len()

2.3. make()

  • 슬라이스에 저장될 요소 개수를 미리 알 수 있다면 make()를 사용해 효율적인 슬라이스 작업을 도모할 수 있다
  • make로 생성한 슬라이스에 append를 사용할 땐 주의할 것

2.4. 슬라이스 선언

  • var data []int => nil
  • var data = []int{} => nil(X)
  • 슬라이스를 아래 방식 중 어떤 걸로 생성할 지 정리해 봅시다.(p.75)
    • 제로 슬라이스
    • 슬라이스 리터럴
    • make()

2.5. 슬라이싱의 슬라이싱

  • 슬라이스 연산자(:)

    • 슬라이스로부터 슬라이스 생성
  • 슬라이스 연산자를 사용하면 복사본을 만들지 않고 메모리를 공유함

  • 슬라이싱과 append를 함께 사용하면 혼란이 가중됨

  • 하위 슬라이스의 수용력
    = 원본 슬라이스의 수용력 - 하위 슬라이스 시작 오프셋

  • 하위 슬라이스와 append를 아무 생각 없이 사용하면 혼란이 가중된다.

    • 의도치 않은 값 변경이 발생
    • 완전한 슬라이스 표현식(full slice expression)으로 해결
  • 완전한 슬라이스 표현식

    • 하위 슬라이스에 얼마나 많은 메모리를 공유할 것인지를 지정
    • 슬라이스 연산때 콜론을 한 번 더 사용하여 세번째 인자에 원본 슬라이스에서 하위 슬라이스의 마지막 요소의 위치 지정
    • 슬라이스 연산의 세 번째 인자의 값을 두 번째 인자의 값과 같도록 설정
      ex)
y := x[:2:2]
z := x[2:4:4]

2.6. 배열을 슬라이스로 변환

  • 배열에 슬라이스 연산 적용
  • 메모리 공유 문제 존재

2.7. copy

  • copy() 내장 함수
    copy(target,source)
    타겟이 소스보다 작으면 타겟크기 만큼만 복사.
  • copy의 첫 번째 인자는 슬라이스이어야 함

3. 문자열과 룬 그리고 바이트

  • 문자열은 룬으로 이루어진 것은 아니다.

  • 문자열은 바이트의 시퀀스이다.

  • 문자열의 길이는 바이트 수

  • 문자열에 슬라이스 연산 사용 가능

    • 문자열은 수정불가(immutable)이므로 슬라이스의 메모리 공유 문제가 없음
    • 유니코드로 구성되므로 슬라이스를 했을 때 문자가 깨지는 우려가 있음
  • 문자열은 바이트 슬라이스 또는 룬 슬라이스로 변환 가능

4. 맵

  • 순서 없는 데이터 처리 유용
  • (Key, Value) Pair
    [SYNTAX]
    var 변수명 map[키타입]값타입
  • 값: 어느 타입도 가능
  • 키: 비교 가능한 타입만 가능(맵, 슬라이스는 탈락)


생성

1) nil 맵(제로 값 할당): map의 제로 값은 nil

  • nil 맵은 길이 0
  • nil 맵의 값을 읽으면 맵 값이 되는 타입의 제로 값
  • nil 맵에 값을 쓰려고 하면 패닉 발생

2) 비어 있는 맵 리터럴: 비어 있는 맵 생성

  • nil 맵과 다르다
  • 길이는 0
  • 비어 있는 맵 리터럴이 할당된 맵을 읽고 쓸 수 있다.

3) 값이 있는 맵 리터럴

  • 키와 값을 콜론으로 구분
  • 마지막 요소(키, 값)의 끝에 콤마(,)를 붙인다.

4) make() 함수로 생성

  • 맵의 요소 개수를 안다면
  • 길이는 0 (make()로 슬라이스 만드는 것과 다름)
  • 초기 지정 개수 이상으로 커질 수 있다.
  • 맵의 제로 값은 nil
  • 맵은 비교 불가능, 단 nil과 같은지 다른지는 비교 가능

4.1 맵 읽고 쓰기

  • := 연산자는 사용 불가
  • 아직 설정되지 않은 키에 할당된 값을 읽으면 값 타입의 제로 값이 반환된다.

4.2 콤마 OK 관용구(idiom)

  • 맵의 키에 대응되는 값이 없어도 제로 값이 리턴되지만
  • 맵에 키가 존재하는지 확인할 필요가 있을 때 주로 사용하는 패턴
    • 맵에 키가 없어서 제로 값이 반환되는 건지
    • 실제로 키가 있는데 해당 값이 제로인 건지
package main

import "fmt"

func main() {

	m := map[string]int{

		"Hello": 5,
		"world": 0,
	}

	v, ok := m["Hello"]
	fmt.Println(v, ok)

	v, ok = m["world"] // 위에 이미 v, ok가 만들어져서 :=사용불가.
	fmt.Println(v, ok)

	v, ok = m["mj"]
	fmt.Println(v, ok)
}

D:\go>go run hello2.go
5 true
0 true
0 false

true ; 해당 키가 맵에 존재한다.


4.3 맵 (요소) 삭제

  • delete() 내장 함수 사용
  • 키가 존재하지 않거나 nil 맵인 경우 아무것도 일어나지 않음
  • delete() 함수는 반환값이 없음

4.4 맵을 셋(집합)으로 이용

  • 집합(셋)
    • Uniqueness, 순서 없음
  • Go는 집합형을 직접 지원하지 않고 맵을 통해 간접적으로 지원
    • 집합의 원소로 쓰고 싶은 타입을 맵의 키 타입으로
    • 맵의 값을 불리언으로 설정

5. 구조체

  • 여러 데이터 타입을 한데 묶어서 다루고 싶을 때 사용
  • struct 키워드, type 키워드
  • 구조체는 사용자 정의 타입
  • 따라서 바로 사용할 순 없고
    1) 구조체 정의 -> 2) 구조체를 타입으로 하는 변수 선언
  • 구조체 항목들은 콤마로 구분하지 않는다
  • 구조체는 어떤 블록 레벨에서도 정의 가능
  • 구조체의 제로 값
  • 구조체를 구성하는 항목들의 제로 값
  • 구조체 리터럴
    • 첫 번째 방법
      • 구조체 항목 값은 구조체 정의한 순서대로 나열
      • 구조체 항목 값은 콤마로 구분하고 마지막 항목에서 콤마를 붙여야 함
    • 두 번째 방법
      • 맵 리터럴과 유사
      • 순서 무관
      • 생략할 경우 제로 값으로 설정
  • 위 두 방법을 혼용할 수 없음
  • 제로 구조체와 비어 있는 구조체는 차이점이 없다
  • 구조체의 멤버(항목)을 접근할 때는 인덱싱이 아니라 점 표기법을 사용

5.1. 익명 구조체

  • 한 번만 사용할 구조체

  • type 생략 -> 구조체 이름이 없다.

  • 구조체 변수만 존재

  • 주요 용도

    • 마샬링, 언마샬링
    • 테스트 작성

5.2. 구조체 비교와 변환

  • 구조체 비교는 항목에 따라 다름
  • 두 개의 구조체가 같은 이름, 순서, 타입으로 구성되어 있으면 구조체 간에 타입 변환 가능

기타

  • 숫자에서 밑줄은 그냥 구분자. 0_0 ; 00

  • null + 연산 => null
    예시 ) null + 1 = null

  • msb 0x1234 lsb

    • 1000
    • 2001
profile
꾸준히, 차근차근
post-custom-banner

0개의 댓글