학습
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
코드 한 줄씩 동작 설명
var a int = 42: 정수형 변수a를 선언하고 값42를 할당합니다.var p *int = &a:p라는 포인터 변수를 선언하고a의 주소를 저장합니다.fmt.Println(p):p가 가리키는 주소에서 값을 읽어 출력합니다. 이때p는 역참조를 통해42를 반환합니다.*p = 100:p가 가리키는 주소의 값을100으로 변경합니다. 이로 인해 변수a의 값도 변경됩니다.fmt.Println(a): 변수a의 값을 출력하여 변경된 값인100을 확인합니다.- 주요 사항: 포인터가 nil인 경우 역참조하면 프로그램이 종료됩니다. 항상 포인터가 nil인지 확인해야 합니다.
- 주요 사항: 포인터를 nil로 만들면 이후 접근 시 문제가 발생합니다. 사용 후 nil로 초기화하는 것은 좋지 않습니다.
- 포인터는 메모리 주소를 저장하여 변수에 직접 접근할 수 있게 해줍니다.
- 포인터를 사용하면 메모리 효율성을 높이고 성능을 개선할 수 있습니다.
- nil 포인터를 조심하고, 항상 포인터가 가리키는 값을 확인해야 합니다.
- 다음에 학습하면 좋은 주제: 슬라이스(Slices)와 맵(Maps), 고루틴(Goroutines) 및 채널(Channels).
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 포인터 역참조: 런타임 패닉 발생
실수 2: 포인터의 주소를 잘못 사용
var a int = 5
var p int = &a
p = nil // p에 nil을 할당
fmt.Println(p) // nil 포인터 역참조: 런타임 패닉 발생