포인터
포인터는 메모리에 있는 일부 데이터에 대한 주소다.
& : 변수나 함수의 주소를 반환하는 연산자
* : 주소의 데이터를 반환하는 연산자 (&의 반대)
var x int = 1
var y int
var ip *int
ip = &x
y = *ip // y == 1
new()
new()는 변수를 만드는 또 다른 방법이다.
변수를 반환하는 대신 변수에 대한 포인터를 반환하는 함수다.
따라서 new()는 변수를 만들고 해당 변수에 대한 포인터를 반환한다.
new()를 사용하면 변수가 기본적으로 0으로 초기화된다.
ptr := new(int)
*ptr = 3
변수의 범위
변수의 범위는 코드에서 변수에 접근할 수 있는 위치다.
따라서 변수 범위는 코드에서 변수 참조가 확인되는 방식을 정의한다.
var x = 1
func f() {
fmt.Printf("%d", x) // 1
}
func g() {
var x = 2
fmt.Printf("%d", x) // 2
}
메모리 할당 해제
Stack : 함수 호출 전용 메모리 영역. 함수의 로컬 변수가 저장된다. 함수가 끝나면 자동으로 할당 해제한다.
Heap : 영구 메모리 영역. 명시적으로 할당을 해제해야 한다.
var x = 1 // Heap에 저장
func f() {
fmt.Printf("%d", x)
}
func g() {
var x = 2 // Stack에 저장
fmt.Printf("%d", x)
}
Go의 경우에는 가비지 컬렉션이 있어서 메모리 할당 해제를 할 필요가 없다.
가비지 컬렉션 (Garbage Collection)
자동으로 메모리 할당 해제를 하는 도구
인터프리터에 의해 수행되기 때문에 느리다.
하지만 Go는 가비지 컬렉션이 컴파일된 언어이기 때문에 빠르다.
Go의 가비지 컬렉션에서는 다음 2가지 특징이 있다.
1. 컴파일러가 stack에 저장할 것인지 heap에 저장할 것인지 결정한다.
2. 백그라운드에서 가비지 컬렉션이 실행된다.
주석
// 한 줄 주석
/*
여러 줄 주석
*/
출력
fmt 패키지를 import 해야 한다.
package main
import "fmt"
func main() {
fmt.Printf("Hi") // Hi
x := "Joe"
fmt.Printf("Hi" + x) // Hi Joe
fmt.Printf("Hi %s", x) // Hi Joe
fmt.Println("Hi") // Hi
}
타입 변환
타입(값) 형식으로 변경할 수 있다.
package main
import (
"fmt"
"reflect"
)
func main() {
var x int = 65
var y string = string(x)
fmt.Println(y, reflect.TypeOf(y)) // A string
}
string
Go는 UTF-8 문자코드를 표준 문자코드로 사용한다.
string은 16byte 문자열이다.
rune은 4byte 문자다. (char 같은 것)
package main
import (
"fmt"
"unicode"
)
func main() {
fmt.Println(unicode.IsDigit('1')) // true
fmt.Println(unicode.IsDigit('a')) // false
fmt.Println(unicode.IsLetter('a')) // true
fmt.Println(unicode.IsLetter('1')) // false
fmt.Println(unicode.IsSpace(' ')) // true
fmt.Println(unicode.IsSpace('a')) // false
fmt.Println(unicode.IsPunct('.')) // true
fmt.Println(unicode.IsPunct('a')) // false
fmt.Println(unicode.IsLower('a')) // true
fmt.Println(unicode.IsLower('A')) // false
fmt.Println(unicode.IsUpper('A')) // true
fmt.Println(unicode.IsUpper('a')) // false
}
- IsDigit(r rune) : r이 숫자면 true
- IsLetter(r rune) : r이 문자면 true
- IsSpace(r rune) : r이 공백이면 true
- IsPunct(r rune) : r이 .이면 true
- IsLower(r rune) : r이 소문자면 true
- IsUpper(r rune) : r이 대문자면 true
package main
import (
"fmt"
"unicode"
"strings"
)
func main() {
var a rune = 'a'
var A rune = 'A'
fmt.Println(a) // 97
fmt.Println(A) // 65
a = unicode.ToUpper('a')
A = unicode.ToLower('A')
fmt.Println(a) // 65
fmt.Println(A) // 97
fmt.Println(strings.ToUpper("a")) // A
fmt.Println(strings.ToLower("A")) // a
}
- ToUpper() : 값을 대문자로 바꿔줌
- ToLower() : 값을 소문자로 바꿔줌
package main
import (
"fmt"
"strings"
)
func main() {
var A string = "A"
var B string = "B"
fmt.Println(strings.Compare(A, B)) // -1
fmt.Println(strings.Compare(A, A)) // 0
fmt.Println(strings.Compare(B, A)) // 1
}
- Compare(a, b) : a < b 면 -1, a == b 면 0, a > b 면 1
package main
import (
"fmt"
"strings"
)
func main() {
var A string = "Hello"
var B string = "He"
var C string = "world"
var D string = "el"
fmt.Println(strings.Contains(A, B)) // true
fmt.Println(strings.Contains(A, C)) // false
fmt.Println(strings.HasPrefix(A, B)) // true
fmt.Println(strings.HasPrefix(A, D)) // false
fmt.Println(strings.Index(A, B)) // 0
fmt.Println(strings.Index(A, C)) // -1
fmt.Println(strings.Index(A, D)) // 1
}
- Contains(s, substr) : s에 substr이 포함되어 있으면 true
- HasPrefix(s, prefix) : s가 prefix로 시작하면 true
- Index(s, substr) : s에서 substr이 시작하는 인덱스를 반환. 없으면 -1
package main
import (
"fmt"
"strings"
)
func main() {
var A string = "Hello"
var B string = strings.Replace(A, "l", "x", 1)
var C string = strings.Replace(A, "l", "x", 2)
fmt.Println(B) // Hexlo
fmt.Println(C) // Hexxo
var a string = " He llo "
var b string = strings.TrimSpace(a)
fmt.Println(b) // "He llo"
}
- Replace(s, old, new, n) : 문자열 s의 처음 나오는 old를 new로 바꾼 문자열을 반환 (반복횟수 n번)
- TrimSapce(s) : s의 앞 뒤 공백이 없어진 문자열을 반환
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
var A string = "123"
i, err := strconv.Atoi(A)
fmt.Println(i, err, reflect.TypeOf(i)) // 123 <nil> int
var A2 string = "123S"
i2, err2 := strconv.Atoi(A2)
fmt.Println(i2, err2, reflect.TypeOf(i2)) // 0 strconv.Atoi: parsing "123S": invalid syntax
var B int = 123
s1 := strconv.Itoa(B)
fmt.Println(s1, reflect.TypeOf(s1)) // 123 string
var C float64 = 123.456
s2 := strconv.FormatFloat(C, 'f', 6, 64)
fmt.Println(s2, reflect.TypeOf(s2)) // 123.456000 string
var D string = "123.456"
f, err := strconv.ParseFloat(D, 64)
fmt.Println(f, err, reflect.TypeOf(f)) // 123.456 <nil> float64
}
- Atoi(s) : 문자열 s를 int형으로 변환하여 반환하고 error가 있다면 같이 반환한다. error가 없으면 <nil> (none 같은 것)을 반환한다.
- Itoa(i) : int형 i를 문자열로 변환하여 반환한다.
- FormatFloat(f, fmt, prec, bitSize) : float형 f를 문자열로 변환하여 반환한다.
- fmt : 실수 형식
- 'b' : -ddddp+=ddd 예) -4503599627370496p-52
- 'e' : -d.dddde+=dd 예) 1.23457e+08
- 'E' : -d.ddddE+=dd 예) 1.23457E+08
- 'f' : -ddd.dddd 예) 1.352
- 'g' : 'e'와 같으며 지수 값이 클 때 사용
- 'G' : 'E'와 같으며 지수 값이 클 때 사용
- prac : 소수점 아래 자리수. -1로 하면 자동으로 적절하게 설정됨
- bitSize : 부동소수점 비트 수. 32 또는 64로 지정한다.
- fmt : 실수 형식
- ParseFloat(s, bitSize) : 문자열 s를 float형으로 변환하여 반환한다.
상수
컴파일 타임에 값을 알 수 있는 표현식. 절대 변하지 않음.
const를 앞에 붙여서 상수로 표현한다.
const x = 1.3
const (
y = 4
z = "Hi"
)
iota()는 상수를 생성하는 데 사용되는 함수다.
각각 달라야하지만 실제 상수값은 중요하지 않는 상수를 만들기 위해 사용한다. (enum과 비슷함)
ex) 월요일, 화요일, 수요일... 1월, 2월, 3월...
package main
import "fmt"
func main() {
type Grades int
const (
A Grades = iota
B
C
D
F
)
fmt.Println(A, B, C, D, F) // 0 1 2 3 4
}
if
package main
import "fmt"
func main() {
x := 1
if x == 0 {
fmt.Println("x is 0")
} else if x == 1 {
fmt.Println("x is 1") // 출력
} else {
fmt.Println("x is neither 0 nor 1")
}
}
switch
package main
import "fmt"
func main() {
x := 1
switch x {
case 1:
fmt.Println("x is 1") // 실행
case 2:
fmt.Println("x is 2")
default:
fmt.Println("x is not 1 or 2")
}
}
break가 없어도 case 문 조건이 성립하는 시점에 종료된다.
package main
import "fmt"
func main() {
x := 1
switch {
case x > 0:
fmt.Println("x is greater than 0") // 실행
case x < 0:
fmt.Println("x is less than 0")
default:
fmt.Println("x is 0")
}
}
tag가 없는 switch도 만들 수 있다.
for
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Printf("%d ", i) // 0 1 2 3 4
}
for {
fmt.Println("Hi") // 무한루프
}
}
package main
import (
"fmt"
)
func main() {
a := [5]int{1, 2, 3, 4, 5}
for _, value := range a {
value *= 10
}
fmt.Println(a) // [1 2 3 4 5]
for index := range a {
a[index] *= 10
}
fmt.Println(a) // [10 20 30 40 50]
}
range 문은 반복을 처리하기 전에 객체를 복사해서 사용한다.
따라서 반복문 안에서 객체의 값을 변경해도 실제 객체의 값은 변경되지 않는다.
때문에 실제 객체의 값을 변경하고자 할 때는 포인터를 이용해서 작업을 처리해야 한다.
Scan
Scanln : 공백으로 입력을 구분하며, 공백을 입력받음
Scan : 공백과 개행문자로 입력을 구분하며, 공백을 입력받지 않음
Scanf : 개발자가 원하는 형태로 입력받음
package main
import "fmt"
func main() {
var x int
fmt.Scanln(&x)
fmt.Println(x) // 공백 입력 받으면 0 (x의 기본 값)
}
package main
import "fmt"
func main() {
var x int
fmt.Scan(&x) // 공백이 아닐 때까지 입력받음
fmt.Println(x)
}
package main
import "fmt"
func main() {
var x int
var y int
fmt.Scanf("%d, %d", &x, &y) // 반드시 "정수, 정수" 형태로 입력해야 함
fmt.Println(x, y)
}