인자를 받는 함수를 호출할 때 해당 인자는 함수로 복사된다.
func zero(x int) { x = 0 } func main() { x := 5 zero(x) fmt.Println(x) // x는 여전히 5 }
이 프로그램에서 zero
함수는 main
함수에 있는 원본 x
변수를 변경하지 않을 것이다. 하지만 그렇게 하고 싶다면? 이렇게 하는 한 가지 방법은 포인터(pointer)라고 알려진 특별한 데이터 타입을 이용하는 것이다.
func zero(xPtr *int) { *xPtr = 0 } func main() { x := 5 zero(&x) fmt.Println(x) // x는 0 }
포인터는 값 자체보다는 값이 저장된 메모리상의 위치를 가리킨다(포인터는 다른 뭔가를 가리킨다). 포인터 (*int
)를 이용하면 zero
함수가 원본 변수를 수정할 수 있게 된다.
Go에서 포인터는 *
(애스터리스크) 문자 다음에 저장된 값의 타입으로 나타낸다. zero
함수에서는 xPtr
이 int
에 대한 포인터에 해당한다.
*
는 포인터 변수를 "역참조(dereference)"하는 데도 사용된다. 포인터를 역참조하면 해당 포인터가 가리키는 값에 접근할 수 있다. *xPtr = 0
이라고 쓰면 "int
값 0을 xPtr
가 참조하는 메모리 위치에 저장하라"라고 말하는 셈이다. 그렇게 하지 않고 xPtr = 0
이라고 쓰면 컴파일로 오류가 발생하는데, xPtr
은 int
가 아니라 또 다른 *int
만 할당할 수 있는 *int
이기 때문이다.
마지막으로 변수의 주소를 구할 때는 &
연산자를 사용한다. &x
는 *int
(int
에 대한 포인터)를 반환하는데, x
는 int
이기 때문이다. 이를 통해 원본 변수의 값을 변경할 수 있다. main
함수에 있는 &x
와 zero
함수에 있는 xPtr
은 동일한 메모리 위치를 참조한다.
포인터를 구하는 또 다른 방법은 내장 new
함수를 사용하는 것이다.
func one(xPtr *int) { *xPtr = 1 } func main() { xPtr := new(int) one(xPtr) fmt.Println(*xPtr) // x는 1 }
new
는 인자로 타입을 하나 받아 해당 타입의 값에 맞는 충분한 메모리를 할당한 후 그것에 대한 포인터를 반환한다.
일부 프로그래밍 언어에서는 new
와 &
를 사용하는 것 사이에 확연한 차이가 있으며 new
로 생성한 것을 나중에 삭제할 때는 굉장히 세심한 주의를 기울여야 한다. Go에서는 상황이 조금 다른데, Go는 가비지 컬렉션(garbage collection)을 지원하는 언어로서 new
로 생성한 것을 아무것도 가리키는 것이 없으면 메모리가 자동으로 정리된다.
포인터는 Go의 내장 타입에 사용되는 일이 드물지만 다음 장에서도 보겠지만 구조체와 함께 사용할 때 특히 유용하다.
변수의 메모리 주소를 구하는 방법은 무엇인가?
포인터에 값을 할당하는 방법은 무엇인가?
새 포인터를 생성하는 방법은 무엇인가?
다음 프로그램을 실행하고 난 후 x의 값은 무엇인가?
func square(x *float64) { *x = *x * *x } func main() { x := 1.5 square(&x) }
두 정수를 교환할 수 있는 프로그램을 작성하라(x := 1; y := 2; swap(&x, &y)
를 실행하면 x=2
이고 y=1
이어야 한다).
← 이전 | 홈 | 다음 → |