🔥 포인터와 함수

325자
4분

함수에서 포인터를 활용하면 효율적이고 유연한 코드를 작성할 수 있어요. 예제 코드를 통해 포인터와 함수의 관계를 살펴보도록 하죠.

package main
 
import (
	"fmt"
	"math"
)
 
type Vertex struct {
	X, Y float64
}
 
func Abs(v Vertex) float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
func Scale(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
 
func main() {
	v := Vertex{3, 4}
	Scale(&v, 10)
	fmt.Println(Abs(v))
}
 
go

위 코드에서 AbsScale 함수가 Vertex 구조체를 다루는 방식에 주목해 보세요.

Abs 함수는 Vertex 타입의 값을 받아서 해당 점의 원점으로부터의 거리를 계산합니다.

func Abs(v Vertex) float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
go
  • vVertex 타입의 값으로 전달되므로, 함수 내에서 v의 값을 변경해도 원본 Vertex에는 영향을 주지 않아요.

반면, Scale 함수는 Vertex 타입의 포인터를 받아서 해당 점의 좌표를 주어진 배율로 조정하죠.

func Scale(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
 
go
  • vVertex의 포인터이므로, v를 통해 원본 Vertex의 값을 직접 변경할 수 있어요.

main 함수에서는 Scale 함수에 &v를 전달하여 v의 포인터를 넘기고 있어요.

func main() {
	v := Vertex{3, 4}
	Scale(&v, 10)
	fmt.Println(Abs(v))
}
 
go
  • Scale 함수가 v의 값을 변경하면, main 함수에서도 그 변경 사항이 반영되죠.

이제 Scale 함수의 매개변수에서 *를 제거해 볼까요? 어떤 일이 벌어질지 예상이 되시나요?

func Scale(v Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
 
go

Scale 함수가 이제 Vertex 값을 직접 받게 되었습니다. 즉, 함수 내에서 v의 값을 변경해도 원본 Vertex에는 영향을 주지 않게 되죠.

main 함수에서 Scale 호출 시 & 연산자를 제거해야 컴파일 에러가 발생하지 않아요.

func main() {
	v := Vertex{3, 4}
	Scale(v, 10)
	fmt.Println(Abs(v))
}
 
go

하지만 이 경우 Scale 함수는 v의 복사본을 받아 작업하므로, 원본 v의 값은 변경되지 않습니다. 결과적으로 Abs 함수에 전달되는 v는 여전히 {3, 4}인 셈이죠.

이렇게 포인터를 활용하면 함수가 원본 값을 직접 변경할 수 있어요. 함수의 매개변수로 값을 전달할지, 포인터를 전달할지는 함수의 의도와 설계에 따라 결정하면 되겠죠?