이 글은 골든래빗 《Tucker 의 Go 언어프로그래밍》의 2단계 써머리입니다.
목차
- 배열
- 구조체
- 포인터
- 문자열
- 패키지
- 숫자 맞추기 게임 만들기
1. 배열
→ 같은 타입의 데이터들로 이루어진 타입
→ 초깃값 지정하지 않을 경우 기본값으로 초기화
var nums [5]int → [0 0 0 0 0]
→ 초깃값 지정
days := [2]string{"monday", "tuesday"} → [monday, tuesday]
→ 지정한 초깃값만 초기화 되고 나머지는 기본값으로 초기화 var temps [5]float64 = [5]float64{24.3, 26.7} → [24.3 26.7 0 0 0]
→ 인덱스로 지청한 값만 초기화
var s = [5]int{1: 10, 3: 30} → [0 10 0 30 0]
→ 배열요소 갯수 생략 가능 (배열요소 개수는 초기화 되는 요소개수와 같음)
package main
import "fmt"
func main() {
x := [...]int{10, 20, 30}
y := [...]int{}
fmt.Println(x, cap(x), len(x))
// [10 20 30] 3 3
fmt.Println(y, cap(y), len(y))
// [] 0 0
// 요소개수 변경 불가
// x = [4]int{1, 2, 3, 4}
// x = [...]int{1, 2, 3, 4}
}
→ 배열 선언시 개수는 항상 상수 (변수값으로 배열 개수 사용불가)
package main
import "fmt"
const Y int = 3
func main() {
x := 5
const z = 7
a := [x]int{1, 2, 3, 4, 5} // ERROR (변수로 선언된것은 배열 길이로 사용 불가)
b := [Y]int{1, 2, 3} // [1 2 3]
c := [z]int{1, 2, 3} // [1 2 3 0 0 0 0]
var d [10]int // [0 0 0 0 0 0 0 0 0 0]
}
→ 연속된 메모리
요소위치 = 배열 시작 주소 + (인덱스 x 타입 크기)
→ 배열 복사: 타입이 같아야한다.
배열 vs 슬라이스
→ 가변적 특성때문에 슬라이스를 주로 사용하게 됨
배열(array) 슬라이스(slice)
타입 | 값 | 참조 |
용량(cap) | 수정불가 | 수정가능 |
길이(len) | 수정불가 | 수정가능 |
비교연산 | 가능 | 불가능 |
호출 | 복사 전달 | 참조 전달 |
range 키워드
→ for 반복문에서 배열, 문자열, 슬라이스, 맵, 채널 등 요소 순회
for index, value := range array {
fmt.Println(index, value)
}
2. 구조체
→ 다른 타입의 값들을 변수 하나로 묶어주는 기능
→ 타입명이 대문자이면 패키지 외부로 공개되는 타입
→ 기본값(zero value)로 초기화
→ 포함된 필드: 구조체 안에 포함된 다른 구조체의 필드명을 생략하는 경우
→ 불필요한 메모리 낭비를 줄이려면 작은 크기 필드값을 앞에 배치
type 타입명 struct {
필드면 타입
}
3. 포인터
→ 가리키는 데이터 타입 앞에 *을 붙여서 선언 var p *int
→ &을 이용해서 데이터 메모리 주소 알수있음 p = &a
→ 기본값 nil: 유효하지 않는 메모리 주솟값
4. 문자열
→ 큰따옴표("): 특수문자 동작, 한줄로 입력(\n사용해서 여러줄 표현)
→ 백쿼트(`): 특수문자 동작X, 여러줄 입력 가능
→ 표준 문자코드: UTF-8 (다국어 문자 지원)
→ 문자열 대소 비교시 문자열 길이와 상관없이 앞글자부터 (같은 위치에 있는 글자끼리) 비교
→ 연산 +, += 사용해서 문자열 연결 가능: string 합 연산시 새로운 메모리 공간 만들어서 두 문자열 합침 (주솟값 변경 -> 메모리 낭비)
→ string 구조(reflect패키지의 StringHeader): Data(unitptr:포인터) & Len(int:길이)
→ 불변(immutable): 일부만 변경 불가
package main
import "fmt"
func main() {
str := "Hello 월드"
runes := []rune(str)
// 총 바이트 길이
fmt.Printf("len(str) = %d\\n", len(str)) // len(str) = 12
// 배열 요소 개수
fmt.Printf("len(runes) = %d\\n", len(runes)) // len(runes) = 8
}
5. 패키지
→ 코드를 묶는 가장 큰 단위 → 함수, 구조체, 상수를 외부에 노출 → 연관된 함수 타압 등의 코드를 묶어서 배포
패키지 종류
→ main: 시작점을 포함한 패키지, main()함수 포함한 패키지
→ fmt: 표준 입출력
→ math: 수학관력
→ http: 인터넷 웹 프로토콜
→ crypto: 암호화 기능
→ net: 네트워크 기능
패키지 사용
→ import + 따옴표
→ 해당 패키지의 외부로 노출하는 함수, 구조체, 변수, 상수 등 사용 가능
→ 변수명, 함수명, 구조체명 첫글자가 대문자인 경우만 외부 노출
→ 패키지 명은 패키지 경로 마지막 폴더
→ 모든 문자 소문자 권장
→ 패키지 전역으로 선언된 첫글자 대문자인 변수, 상수, 타입, 함수, 메서드, 구조체, 필드명: 패키지 외부 공개
→ 함수 내부에 선언된 상수는 패키지 외부 공개 불가 (공개되는 함수일 경우에도 불가능)
→ 한개 패키지 사용 import "fmt"
→ 여러 패키지 사용: import ("패키지이름", "패키지이름")
import (
"fmt"
"os"
)
→ 겹치는 패키지: 패키지명 앞에 별칭 붙여서 사용
import (
"text/template"
htemplate "html/template"
)
패키지 설치
→ 기본 패키지: Go설치 경로에 포함
→ 깃허브와 같은 외부 저장소 패키지: 다운 후 GOPATH\pkg폴더
→ 모듈아래 위치한 패키지: 현재폴더
GO 모듈
→ Go 패키지들을 모아놓은 Go프로젝트 단위
→ Go 모듈 사용 기본 (Go1.16버전 이상)
→ go build하려면 Go 모듈 루트 폴더에 go.mod 파일 필수
→ go mod init 패키지명: go.mod파일 생성
→ go mod tidy: Go모듈에 필요한 패키지 찾아서 다운로드 & 필요한 패키지 정보 go.mod, go.sum 입력
→ 모듈명은 다른 외부 패키지 이름과 겹치지 않게 주의
→ 모듈명의 마지막 이름은 폴더명과 맞추기
go build
→ Go 모듈 루트 폴더에 go.mod 파일 필수
→ 실행파일 만들때 go.mod(모듈 이름과 Go버전, 필요한 외부 패키지 등 명시)와 go.sum(외부패키지 버전 정보) 통해 외부 패키지와 모듈 내 패키지 합쳐서 실행파일 생성
패키지 초기화
→ 패키지 import시 패키지의 모든 전역변수 초기화 후 init()함수 호출
→ init(): 입력 매개변수, 반환값 없는 함수
→ init()만 사용할 경우 밑줄_ 이용해 불러오기
숫자 맞추기 게임 만들기
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"time"
)
// 숫자 맞추기 게임
// - rand() 랜덤값 생성 패키지
// - rand.Intn() 0~N-1 사이의 값 생성
var stdin = bufio.NewReader(os.Stdin)
func InputIntValue() (int, error) {
var n int
_, err := fmt.Scanln(&n)
if err != nil {
stdin.ReadString('\\n')
}
return n, err
}
func main() {
// 랜덤한 숫자 생성 (0-99사이의 숫자)
randTest := rand.Intn(100)
fmt.Println("seedX :", randTest)
// rand.Seed()가 0이라서 일정한 값 호출
// 내부에 있는 시퀀스가 같아서 매번 호출할때마다 똑같음
// 시드값(기반)을 주면 랜덤숫자 변경
// 시드값도 똑같으면 값이 똑같음
// 시드값이 바꿔져야 결과값이 바뀜
rand.Seed(time.Now().UnixNano())
// 랜덤시드로 하면 좋은것? -> 매번 변화하는 값: Time
// time.Now().UnixNano() : t의 1970년 1월 1일 부터 현재까지 경과된 시간을 nano sec단위로 반환
r := rand.Intn(100)
cnt := 1
fmt.Println("답 :", r)
// 숫자 맞추기 게임
for {
fmt.Printf("숫자값을 입력하세요: ")
n, err := InputIntValue()
if err != nil {
fmt.Println("숫자만 입력하세요")
} else {
// fmt.Println("입력하신 숫자는 ", n, " 입니다.")
// fmt.Println(r)
if n > r {
fmt.Println("입력하신 숫자가 더 큽니다.")
} else if n < r {
fmt.Println("입력하신 숫자가 더 작습니다.")
} else {
fmt.Println("숫자를 맞췄습니다. 축하합니다. 시도한 횟수: ", cnt)
break
}
cnt++
}
}
}
'STUDY > Go Lang' 카테고리의 다른 글
[3단계] Go 프로그래밍에 유용한 기법 익히기 (2) (0) | 2023.11.13 |
---|---|
[3단계] Go 프로그래밍에 유용한 기법 익히기 (1) (0) | 2023.11.05 |
[Go Lang] 고급 기법으로 Go 레벨업하기 (2) (0) | 2023.10.29 |
[GoLang] 고급 기법으로 Go 레벨업하기 (1) (1) | 2023.10.22 |
[GoLang] 가볍게 Go 입문하기 (1) (0) | 2023.10.07 |