IMCAT.DEV
로그인 가입
Go 입문 자료구조

Go 포인터 (Pointers)

Go 포인터 (Pointers) — 초급

포인터 (Pointers)

1. 도입 (왜 배워야 하는가)

포인터는 Go 프로그래밍 언어에서 메모리 관리를 효율적으로 할 수 있도록 도와주는 중요한 개념입니다. 포인터를 이해하지 못하면, 변수의 값이 어떻게 저장되고 전달되는지에 대한 이해가 부족해질 수 있습니다. 이는 특히 대규모 애플리케이션에서 성능 저하를 초래할 수 있습니다.

실무에서 포인터를 제대로 활용하지 않으면, 메모리 낭비나 성능 저하, 심지어 프로그램의 예기치 않은 동작을 유발할 수 있습니다. 예를 들어, 큰 데이터를 함수에 복사하는 대신 포인터를 통해 직접 접근할 수 있다면, 성능을 크게 개선할 수 있습니다.

2. 핵심 개념 설명

포인터는 메모리 주소를 저장하는 변수입니다. 포인터를 통해 변수의 메모리 주소를 참조하고, 이를 사용하여 데이터를 직접 변경할 수 있습니다.

실생활 비유

포인터를 집 주소에 비유할 수 있습니다. 집 주소는 집의 위치를 알려주지만, 그 주소를 통해 직접 집에 들어가서 물건을 가져오거나 변경할 수 있습니다. 포인터는 이와 같이 값에 직접 접근할 수 있는 '주소' 역할을 합니다.

관련 용어 정리

용어설명
포인터메모리의 주소를 저장하는 변수
역참조포인터가 가리키는 주소에서 값을 읽거나 쓰는 것
nil 포인터아무것도 가리키지 않는 포인터

3. 기본 코드 예제

package main

import "fmt"

func main() {
    var a int = 42       // 정수형 변수 a를 선언하고 값 42 할당
    var p int = &a     // a의 주소를 p에 저장 (포인터)
    
    fmt.Println(p)      // p가 가리키는 주소의 값을 출력 (42)
    
    *p = 100            // p가 가리키는 주소의 값을 100으로 변경
    fmt.Println(a)      // a의 값을 출력 (100)
}

실행 결과

42
100

코드 한 줄씩 동작 설명

  1. var a int = 42: 정수형 변수 a를 선언하고 값 42를 할당합니다.
  2. var p *int = &a: p라는 포인터 변수를 선언하고 a의 주소를 저장합니다.
  3. fmt.Println(p): p가 가리키는 주소에서 값을 읽어 출력합니다. 이때 p는 역참조를 통해 42를 반환합니다.
  4. *p = 100: p가 가리키는 주소의 값을 100으로 변경합니다. 이로 인해 변수 a의 값도 변경됩니다.
  5. fmt.Println(a): 변수 a의 값을 출력하여 변경된 값인 100을 확인합니다.
  6. 4. 실전 활용 예제

    예제 1: 장바구니 구현

    쇼핑몰에서 장바구니를 구현할 때, 여러 상품을 담기 위해 포인터를 사용하여 상품의 수량을 업데이트할 수 있습니다.

    package main
    
    import "fmt"
    
    type Product struct {
        name  string
        price float64
    }
    
    func updateQuantity(p int, value int) {
        p += value // 포인터를 통해 수량 변경
    }
    
    func main() {
        quantity := 2
        fmt.Println("현재 수량:", quantity)
    
        updateQuantity(&quantity, 3) // 수량을 3 증가
        fmt.Println("업데이트된 수량:", quantity)
    }

    예제 2: 구조체 수정

    구조체를 사용하여 여러 데이터를 묶고 포인터로 효율적으로 수정할 수 있습니다.

    package main
    
    import "fmt"
    
    type User struct {
        name string
    }
    
    func changeName(u *User, newName string) {
        u.name = newName // 포인터를 통해 이름 변경
    }
    
    func main() {
        user := User{name: "Alice"}
        fmt.Println("이전 이름:", user.name)
    
        changeName(&user, "Bob") // 이름을 "Bob"으로 변경
        fmt.Println("변경된 이름:", user.name)
    }

    5. 흔한 실수와 주의사항

    실수 1: nil 포인터 역참조

    var p int
    fmt.Println(p) // nil 포인터 역참조: 런타임 패닉 발생
    • 주요 사항: 포인터가 nil인 경우 역참조하면 프로그램이 종료됩니다. 항상 포인터가 nil인지 확인해야 합니다.
    • 실수 2: 포인터의 주소를 잘못 사용

      var a int = 5
      var p int = &a
      p = nil // p에 nil을 할당
      fmt.Println(p) // nil 포인터 역참조: 런타임 패닉 발생
    • 주요 사항: 포인터를 nil로 만들면 이후 접근 시 문제가 발생합니다. 사용 후 nil로 초기화하는 것은 좋지 않습니다.
    • 6. 정리

    • 포인터는 메모리 주소를 저장하여 변수에 직접 접근할 수 있게 해줍니다.
    • 포인터를 사용하면 메모리 효율성을 높이고 성능을 개선할 수 있습니다.
    • nil 포인터를 조심하고, 항상 포인터가 가리키는 값을 확인해야 합니다.
    • 다음에 학습하면 좋은 주제: 슬라이스(Slices)와 맵(Maps), 고루틴(Goroutines) 및 채널(Channels).