Go 2. 기본 문법2

jiffydev·2020년 12월 30일
0

반복문

go에서 반복문을 사용할 때는 for ... range 를 사용하는데 다음과 같이 사용된다.

func superAdd(numbers ...int) int {
	total := 0
	for _, number := range numbers {
		total += number
	}
	return total
}

func main() {
	fmt.Println(superAdd(1, 2, 3, 4, 5, 6))
}

>> 21

range는 항상 인덱스와 값(map이라면 key, value)를 리턴하므로 for 다음의 변수는 항상 두개가 필요하다. 만약 하나가 필요 없다면 '_'로 처리하면 된다.

또한 다른 프로그래밍 언어(C, Java, JavaScript 등)에서 사용하는 반복문과 비슷한 형식으로도 반복문을 만들 수 있다.

func main(){
  for i := 0; i < 5; i++ {
          fmt.Println(i)
      }
}
    
>> 0
>> 1
>> 2
>> 3
>> 4

조건문

조건문은 여타 프로그래밍 언어와 작성법이 크게 다르지 않다.

func canIDrink(age int) bool {
	if age < 18 {
		return false
	}
	return true
}

func main(){
	fmt.Println(canIDrink(16))
}

>> false

다만 특기할만한 점으로, if 조건 안에서 변수를 생성해 그 변수와 조건을 비교할 수 있다는 특징이 있다.

func canIDrink(age int) bool {
	// variable expression
	if koreanAge := age + 2; koreanAge < 18 {
		return false
	}
	return true
}

func main(){
	fmt.Println(canIDrink(16))
}

>> false

포인터

포인터는 어떤 변수가 있을 때 그 변수의 값이 아닌 메모리 주소를 가리키는 것을 뜻하며, 한 변수를 다른 변수에 복사할 때 반드시 이해하고 있어야 하는 개념이다.
예를 들어

func main(){
  a := 2
  b := a
  a = 10
  fmt.Println(a,b)
}

의 출력값은 10 2이다. 그 이유는 변수 b에는 a가 2로 초기화되었을 때의 값만 저장되어 있고, a값이 10으로 변경된 결과는 저장되어 있지 않기 때문이다.(서로 다른 메모리 주소)

그런데 만약 a의 값이 엄청나게 큰 값이고 이를 b에 그대로 복사하게 되면 같은 내용인데 용량을 두배로 차지하게 되므로 메모리의 낭비가 될 것이다.

따라서 b를 a의 값이 아닌 a의 메모리 주소를 바라보게 하면 a,b는 항상 같은 값을 가지면서 메모리는 a의 메모리만을 사용하므로 낭비가 없어진다. 이 때 사용하는 것이 포인터로, C 등을 배웠다면 이미 익숙한 개념일 것이다.

포인터는 변수에 저장될 값 앞에 '&'를 붙여주면 되는데, 이를 출력해보면 메모리 주소값이 나온다. 우리가 필요한 것은 결국 메모리에 저장된 값이므로, 값을 출력할 때는 변수 앞에 '*'를 붙여서 출력한다.

func main(){
  a := 2
  b := &a
  fmt.Println(a, b)
  fmt.Println(a, *b)
}

// b의 값은 메모리 주소값이므로 사용자에 따라 다른 값이 나온다
>> 2 0xc0000140f0
// 변수 앞에 *를 붙여주면 값을 출력
>> 2 2

또한 a와 b는 메모리 주소를 공유하고 있으므로, a의 값을 바꾸면 b의 값도 바뀌고 심지어 b의 값을 바꿔도 a 값이 바뀐다.

func main(){
  a := 2
  b := &a
  a = 10
  fmt.Println(a, *b)
  
  // b의 값을 바꿀 때는 반드시 *를 붙여야 한다
  *b = 20
  fmt.Println(a, *b)
}

>> 10 10
>> 20 20

array, slice

go에서 배열은 두 종류가 있는데 간단하게 설명하자면 array는 크기 제한이 있는(있어야 하는) 배열, slice는 크기를 자유롭게 바꿀 수 있는 배열이라고 할 수 있다.
따라서 array는 크기를 지정해서 초기화 해야 하며 정해진 크기 이상으로 추가할 경우 에러가 난다.

// Array
func main() {
	lst := [2]int{1}
	fmt.Println(lst)
    lst[1] = 2
    fmt.Println(lst)
}

// 배열에서 지정한 크기보다 원소의 숫자가 적으면 int의 경우 0, string의 경우 공백이 된다
>> [1 0]
>> [1 2]

slice는 크기를 정하지 않고 자유롭게 추가가 가능한데, 원소를 추가할 때는 append 메소드를 사용한다. append메소드는 (slice이름, 추가할 내용)을 인자로 받고 기존의 슬라이스에 추가하는 것이 아니라 새로운 슬라이스를 리턴한다. 따라서 반드시 변수에 할당해 주어야 사용할 수 있다.

// Slice
func main() {
	names := []string{"a", "b"}
    fmt.Println(names)
    // names 변수에 재할당
    names = append(names, "lala")
    fmt.Println(names)
}

>> [a b]
>> [a b lala]

maps

go의 map은 javascript의 객체, python의 딕셔너리와 유사하다. 차이점은 후자에서는 key, value에 어떤 자료형이 들어가도 상관없지만 map에서는 타입을 초기화하고 지정한 타입만 들어갈 수 있다는 점이다.

func main() {
	person := map[string]string{"name": "nico", "age": "12"}
	fmt.Println(nico)
}

>> map[age:12 name:nico]

여기서 age의 값을 int형인12로 바꾸면 에러가 발생하므로 주의

structs

struct는 구조체라고 하는데 관련된 정보를 하나로 묶으면서 자료형을 key에 따라 다르게 줄 수 있다. 따라서 map보다는 자료형의 제약에서 자유롭다.

type person struct {
	name    string
	age     int
	favFood []string
}

func main() {
	favFood := []string{"abc", "def"}
    someone := person{name: "nico", age: 18, favFood: favFood}
    fmt.Println(someone)
}

>> {nico 18 [abc def]}

여기서 변수에 할당하는 구조체는 key 없이 person{"nico", 18, favFood}으로도 같은 결과가 나온다. 하지만 key가 무엇인지 매번 선언된 struct를 봐야 하므로 같이 써 주는 것이 좋다.

profile
잘 & 열심히 살고싶은 개발자

0개의 댓글