Golang Basic


상수
  • const 키워드 사용
  • const 키워드를 생략할 수 없기 떄문에 ‘:=’ 이용한 타입 추론 불가능.
  • 괄호로 여러개 상수를 묶어 초기화 가능(상수 선언간 개행 필요.)
const (
	val1 = 1
	val2 = 2
)

증감연산자
  • 전위 연산자 사용 불가능
  • 정수, 실수, 복소수에 대해 증감 연산 가능

논리연산자
  • bool형(true,false)에 대해서만 논리 연산 가능, 다른 언어 처럼 1과 0에 대해서 논리연산 불가능.

포인터
  • C처럼 포인터 사용 가능. & (메모리 주소 참조), *(메모리에 접근, 값 참조)
  • 포인터 산술 연산은 제공하지 않음… 그럼 뭐에다 쓰려고…

자료형


Sizeof
  • Unsafe 패키지의 Sizeof 함수로 타입 size 확인 가능
Boolean Type
  • 1 Byte
  • Go에서는 true와 false만 사용하여 값 할당
Integer Type
  • 32bit 시스템에서 32비트, 64bit 시스템에서 64비트
  • int8, int16, int32, int64, uint8 … uintptr(8Byte)
  • uintptr은 포인트 주소 할당할 때 주로 사용.
Floating Point Type
  • float32, float64
문자열 타입
  • 16 Byte
  • string으로 선언한 문자열 타입은 immutable 타입으로, 값 수정 불가능
etc.
  • byte (1Byte), 바이트 값을 8bit uint와 구별해 사용
  • rune (4Btye), 문자 값을 정수 값과 구별하기 위해 사용
자료형 Casting
  • 명시적 형변환 해줘야함
var str string = "Hello"
changeStr := []byte(str) // string -> byte 배열
str2 := string(changeStr)

for


  • Golang은 while 지원하지 않음. 오로지 for
기본 형태
for i:= 0 ; i < n; i++ {
	...
}
무한 loop
for {
    ...
}
for range
  • foreach와 비슷, for index, 요소값 := range 컬렉션 이름

조건, 제어문


  • Only True, False, Not 1,0
  • Go에서는 조건문 한 줄일때도 중괄호 생략 불가능
  • 괄호 시작과 else문 같은 줄에.
Break, Continue, Goto
  • break, continue 키워드로 사용 가능
  • break문 뒤에 레이블과 같이 사용되면 지정된 레이블로 이동 가능
func main() {
	i := 0
LABEL1:
	for {
		if i == 0 {
			break LABEL1
		}
	}

// break문이 바로 빠져나왔던 for문 다음 문장 실행
	fmt.Println("End")
	goto LABEL1
}

Collection


  • Go에서 두 개 이상의 변수를 모아 놓은 것을 컬렉션이라..
Array
  • Go에서 배열은 정적. 고정된 크기. 그래서 배열 크기를 동적으로 사이징하거나 부분 배열만 가져오는 기능은 없다.
  • 배열의 크기는 자료형을 구성하는 하나의 요소로, 2[int]와 3[int]는 자료형 자체가 다른 것이다.
  • var arrName [사이즈]자료형
  • 자바나 C++처럼 배열 크기를 데이터 타입 앞에 쓰는 방식 안됨.
func main(){
	var arr1 [5]int
	fmt.Println(arr1)
	// [0 0 0 0 0] 출력
	arr2 := [5]int{1, 2, 3, 4, 5} // type 추론 가능
	fmt.Println(arr1)
	// [1 2 3 4 5]
	var arr3 = [...]int{9, 8, 7, 6} // [...] 이용해 배열 크기 자동 설정
	fmt.Println(len(arr3))
	// 4
}
Slice
  • Go에서 배열은 필요에 따라 동적으 크기를 증가시키는 등의 기능을 가지고있지 않다. Slice는 배열과 다르게 고정된 크기를 미리 지정하지 않아도 필요에 따라 크기를 동적으로 변경할수 있고 부분 추출이 가능하다.

Image

  • Slice는 초기화하지 않아도 배열 위치를 가리키는 ptr, 배열 길이 len, 용량인 cap 변수를 위한 메모리를 가지고 있다.

  • Slice는 참조 타입, 참조하고 있는 slice의 값을 변경하게 되면?

func main(){
	var a []int
	a = []int{1,2,3,4,5}
	a[0] = 10
	a[1] = 20
	a[2] = 30
	a[3] = 40
	a[4] = 50
	b := a[2:4]
	b[1]= 45
	fmt.Println(a)
	// [10 20 30 45 50] 출력
}
make()
  • make(type, length, capacity) 형태로 선언
func main(){
	a := make([]int, 0, 2)
	for i:=0 ; i < 9 ; i++ {
		a = append(a, i)
		fmt.Println("len :", len(a), "cap :", cap(a))
	}
}

output 
len : 3 cap : 4
len : 4 cap : 4
len : 5 cap : 8
len : 6 cap : 8
len : 7 cap : 8
len : 8 cap : 8
len : 9 cap : 16
  • slice에 메모리 공간이 추가될 때마다 다른 메모리에 기존+새로운 공간 크기만큼의 메모리를 할당하고 원본을 복사하는식으로 동작하는데, 매번 공간 할당과 원본 데이터를 복사하면 비효율적이기 때문에 capacity를 두배씩 늘려주는게 아닌가 싶은데, 기초다지고 다시 알아보기로.

  • append()로 slice에 slice 추가 가능

sliceA := []int{1,2}
sliceB := []int{4,5}
slice A = append(sliceA, sliceB...)
  • copy()
func main(){
	sliceA := []int{0,1,2,3,5,6,7}
	sliceB := make([]int, len(sliceA), cap(sliceA)*2)

	copy(sliceB, sliceA)

	sliceC := sliceB[2:4] // 범위 복
	sliceD := sliceB[3:] //인덱스 3부터 끝까지 복사
}
Map
  • Slice 처럼 참조타입
  • var apName map[keyType]valType
  • 선언만하고 초기화하지 않았다면 Nil map
func main() {
	var mp map[int]string
	if mp == nil{
		fmt.Println("nil map")
	}

	var mp2 = map[string]string{
		"foo": "bar",
		"bar": "foo",
	}
  • “mapName[key] = value” 형식으로 값 추가 가능, key 존재한다면 갱신됨
  • “delete(mapName,key)”로 key에 매칭되는 value 삭제
Map의 Key 체크
  • “mapName[key]”는 key에 저장된 값을 반환할 뿐만 아니라, 해당 키에 값이 존재하는지 안 하는지 여부도 함께 반환.
  • console 출력 함수에 mapName[key]를 입력할 때는 key값에 해당하는 value 값만 출력
  • value값과 true/false 값을 반환하기 위해서는 변수 두 개를 선언한 후에 각각 할당 받아야함.
  • value 값만 반환받으려면 변수 한 개만 선언해 할당 받으면 된다.
  • key 포함 여부만 반환 받으려면 “_, bool변수” 형식으로 받으면됨
func main(){
	var mp = make(map[string]string)
	mp["02"] = "Seoul"
	mp["031"] = "Kyeogki"
	mp["053"] = "Daegu"

	val, exist := mp["02"]

	val = mp["053"] // value만 반환
	_, exist = mp["053"] // true, false만 반환
}
클로저
  • 함수 안에서 익명 함수를 정의해서 바깥쪽 함수에 선언한 변수에도 접근할 수 있는 함수
func main(){
	a, b:= 10, 20
	str := "Hello"

	result := func() int {
		return a+b
	}()
	
	func() {
		fmt.Println(str)
	}()
	fmt.Println(result)
}
  • 함수 안세어 함수를 정의하기 위해서 익명 함수만 사용 가능. main() 함수 내에 선언된 익명 함수들이 main() 함수의 변수를 파라미터 없이 접근.
func next() func() int {
	i := 0
	return func() int {
		i += 1
		return i
	}
}

func main() {
	nextInt := next()

	fmt.Println(nextInt())
	fmt.Println(nextInt())
	fmt.Println(nextInt())
	
	nextInt := next()
	fmt.Println(nextInt())
} // 1 2 3 1 출력
  • next 함수는 i 변수를 1씩 증가시키는 익명 함수를 반환. 따라서 nextInt를 실행할 때마다 값이 초기화 되지 않고 이전 컨택스트에서 i 값을 1 증가 시킴.

구조체, 메소드

  • Golang은 객체 지향이지만 전통적인 객체 지향 언어의 Class, Onject, Inheritance 개념 없이, 유사한테로 따름.
  • Golang에서 클래스는 Custom 타입을 정의하는 구조체에 필드만을 표현하고, 메소드는 따로 분리하여 정의함.
type perseon struct {
	name 	 string
	age		 int
	contact  string
}

func main(){
	var p = person{}
	p.name = "lee"
	p.age = 29
	p.contatct = "010-9999-9999"

	p2 := person{"kim", 29, "010-9999-8888"}

}
  • golang의 구조체는 ‘mutable’ 개체로 필드 값 변경시 별도의 새 개체를 만들지 않고 해당 개체 메모리에서 변경
func addAgeRef(pp *person) { // pass by ref
	pp.age += 1 //자동 역참조 * 생략
}
func addAgeVal(p person) { // pass by val
	p.age += 1
}

func main(){
	var pp = new(person) // 포인터 구조체 객체 생성
	pp.age = 29
	addAgeRef(pp) // &을 쓰지 않아도됨
	fmt.Println(pp.age)

	var p = person{}
	p.age = 29
	addAgeVal(p)
	fmt.Println(p.age)
	addAgeRef(&p) // &로 주소 넘기면 pass by ref 가능
	fmt.Println(p.age)
}
  • 구조체 포인터 생성 방법 1. ‘new(구조체이름)’, 2. 구조체 이름 앞에 & 붙이기
  • 포인터 구조체는 선언시 자동으로 역참조되어, 함수 내에서 * 연산자를 사용할 필요가 없음.

생성자(Constructor) 함수

  • 구조체 필드 자체가 사용 전에 초기화되어햐 하는 경우를 위해 ‘생성자 함수’를 사용할 수 있음.
  • 생성자 함수는 구조체 객를 생성하고 초기화한 구조체를 반환
type mapStruct struct{
	mapData map[int]string
}

func newStruct() *mapStruct {
	d := mapSturct{}
	d.mapData = map[int]string{}
	return &d
}