go - pointer
go - pointer
- 대학교 1학년 때 태어나서 처음으로 c programming을 배웠던 시절에 pointer는 뭔가 악몽 같았습니다. 실제로 많은 아이들이 떠났지만, 요즘은 오히려 pointer가 있는 것이 더 익숙하게 느껴질 때도 있어요. pointer가 있으면 이것은 reference를 참조하는 형태, 없으면 value를 참조하는 형태라는 것이 분명히 나누어지기에 가끔은 이게 더 문법 상에 정보 값을 많이 담는 것이 아닌가? 라는 생각이 들곤 합니다.
- 아무튼, golang을 공부하다 보니 오랜만에 pointer 를 공부하게 되네요. 문법 상으로는 c와 유사한데, arrow operator인
->
가 없는 게 조금은 낯설게 느껴집니다.
go - pointer - basic
- pointer variable은 변수의 주소값을 기억하는 변수입니다. 혹은 변수를 가리키는 새로운 변수, 변수의 별명 이라고 생각하셔도 상관없습니다.
- 간단하게
*int
의 형태로 해당 변수가 pointer variable임을 선언할 수 있습니다.
package main
import "fmt"
func main() {
var a int = 10
// var b int = 20
var p1 *int
if p1 == nil {
fmt.Println("pointer variable default value is nil")
}
p1 = &a
fmt.Printf("a: %d \n", *p1)
a = 20
fmt.Printf("a: %d \n", *p1)
*p1 = 30
fmt.Printf("a: %d \n", *p1)
/*
a: 10
a: 20
a: 30
*/
}
go - pointer with struct
- 당연하지만
struct
에 대해서도 pointer 형식으로 메모리 주소값을 찾아서 필요한 메모리를 할당하고 값을 assign하고 변경하는 것이 가능합니다.
package main
import "fmt"
type User struct {
id string
}
func main() {
var user User = User{"frhyme"}
var userP1 *User = &user
fmt.Printf("%s \n", user.id)
fmt.Printf("%s \n", *(&userP1.id))
/*
userP1은 pointer variable 인데도,
-> 형태의 arrow operator를 사용하지 않고,
value variable과 동일한 . operator를 사용하여 하위 field를 찾습니다.
*/
fmt.Printf("%s \n", userP1.id)
/*
pointer variable을 통해 값을 변경할 수 있습니다.
*/
userP1.id = "new_frhyme"
fmt.Printf("%s \n", user.id)
fmt.Printf("%s \n", *(&userP1.id))
fmt.Printf("%s \n", userP1.id)
/*
pointer variable을 선언하고 memory를 할당할 수 있습니다.
*/
var userP2 = new(User)
userP2.id = "new_new_frhyme"
fmt.Printf("%s \n", userP2.id)
}
golang - without arrow operator
- golang에서는 value type이든, reference type(pointer)든 관계없이 하위 field를 지정할 때는 모두
.
를 사용합니다. clang에서는->
이라는 arrow operator가 존재하고, 이를 사용해서 하위 field 에 접근할 수 있습니다.
clang - arrow operator
- 아래 code에서
p1
은 pointer variable이기 때문에 하위 field에 접근하기 위해서는->
를 사용해야 합니다.
#include <stdio.h>
typedef struct User {
int id;
} User;
int main(void) {
User u1;
User* p1 = &u1;
u1.id = 10;
/*
u1 은 value이기 때문에 . 을 통해 field에 접근하는 반면,
p1의 경우 reference variable이기 때문에 해당 주소 값에 할당된 memory를 찾고,
해당 memory 내에 id 값을 찾는 방향으로 진행된다.
*/
printf("== Output \n");
printf("%d \n", u1.id);
printf("%d \n", p1->id);
printf("%d \n", (*p1).id);
printf("%d \n", (*(&u1)).id);
/*
== Output
10
10
10
10
*/
return 0;
}
- 반면 go 에서는
->
arrow operator를 사용하지 않고 모두.
dot operator를 사용해서 처리할 수 있죠. 개발자 입장에서는 몹시 편합니다. 이유를 go.dev - Selectors에서 확인해 보면 다음과 같은데요, compiler가 알아서 pointer variable을 dereferencing 해서 처리해준다, 라고 해석하면 될 것 같네요.
As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
Wrap-up
- dot operator, arrow operator를 구분하지 않고 field에 접근할 수 있기 때문에 c에 비해서 개발 생산성이 조금은 올라갈까요? 흠.
댓글남기기