Skip to content

Go 포인터

Go 는 포인터를 유지하여 일정 수준 성능을 보장하면서도, 더 나은 GC 와 안전을 위해 포인터 사용을 제한합니다.

생성

포인터에는 두 가지常用的 연산자가 있습니다. 하나는 주소 연산자 & 이고, 다른 하나는 역참조 연산자 * 입니다. 변수의 주소를 취하면 해당 타입의 포인터를 반환합니다. 예를 들어:

go
func main() {
   num := 2
   p := &num
   fmt.Println(p)
}

포인터는 변수 num 의 주소를 저장합니다.

0xc00001c088

역참조 연산자는 두 가지 용도가 있습니다. 첫 번째는 포인터가 가리키는 요소에 액세스하는 것, 즉 역참조입니다. 예를 들어

go
func main() {
  num := 2
  p := &num
  rawNum := *p
  fmt.Println(rawNum)
}

p 는 포인터이며, 포인터 타입을 역참조하면 포인터가 가리키는 요소에 액세스할 수 있습니다. 또 다른 용도는 포인터를 선언하는 것입니다. 예를 들어:

go
func main() {
   var numPtr *int
   fmt.Println(numPtr)
}
<nil>

*int 는 해당 변수의 타입이 int 타입 포인터임을 나타냅니다. 하지만 포인터는 선언만 해서는 안 되며, 초기화해야 하고 메모리를 할당해야 합니다. 그렇지 않으면 빈 포인터가 되어 정상적으로 사용할 수 없습니다. 주소 연산자를 사용하여 다른 변수의 주소를 포인터에 할당하거나, 내장 함수 new 를 사용하여 수동으로 할당해야 합니다. 예를 들어:

go
func main() {
   var numPtr *int
   numPtr = new(int)
   fmt.Println(numPtr)
}

대부분 단변수를 더 많이 사용합니다.

go
func main() {
   numPtr := new(int)
   fmt.Println(numPtr)
}

new 함수는 타입이라는 하나의 매개변수만 가지며 해당 타입의 포인터를 반환합니다. 함수는 해당 포인터에 메모리를 할당하며, 포인터는 해당 타입의 영값을 가리킵니다. 예를 들어:

go
func main() {
   fmt.Println(*new(string))
   fmt.Println(*new(int))
   fmt.Println(*new([5]int))
   fmt.Println(*new([]float64))
}

0
[0 0 0 0 0]
[]

포인터 연산 금지

Go 에서는 포인터 연산을 지원하지 않습니다. 즉, 포인터는 오프셋할 수 없습니다. 먼저 C++ 코드를 보겠습니다.

cpp
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *p = &arr[0];
    cout << &arr << endl
         << p << endl
         << p + 1 << endl
         << &arr[1] << endl;
}
0x31d99ff880
0x31d99ff880
0x31d99ff884
0x31d99ff884

배열의 주소와 첫 번째 요소의 주소가 일치하며, 포인터에 1 을 더한 연산 후 두 번째 요소를 가리키는 것을 알 수 있습니다. Go 의 배열도 이와 동일하지만, 포인터는 오프셋할 수 없다는 점이 다릅니다. 예를 들어

go
func main() {
   arr := [5]int{0, 1, 2, 3, 4}
   p := &arr
   println(&arr[0])
   println(p)
   // 포인터 연산 시도
   p++
   fmt.Println(p)
}

이러한 프로그램은 컴파일을 통과할 수 없으며, 오류는 다음과 같습니다.

main.go:10:2: invalid operation: p++ (non-numeric type *[5]int)

TIP

표준 라이브러리 unsafe 는 로우 레벨 프로그래밍을 위한 많은 연산을 제공하며, 그중에는 포인터 연산도 포함됩니다. 세부사항은 표준 라이브러리 -unsafe 에서 확인하실 수 있습니다.

new 와 make

이전 몇 절에서 내장 함수 newmake 를 여러 번 언급했습니다. 두 함수는 비슷하지만 다르기도 합니다. 아래에서 복습하겠습니다.

go
func new(Type) *Type
  • 반환값은 타입 포인터입니다.
  • 매개변수는 타입입니다.
  • 포인터에 메모리 공간을 할당하는 데 전용됩니다.
go
func make(t Type, size ...IntegerType) Type
  • 반환값은 값이며 포인터가 아닙니다.
  • 첫 번째 매개변수는 타입이며, 가변 길이는 전달되는 타입에 따라 다릅니다.
  • 슬라이스, 맵, 채널에 메모리를 할당하는 데 전용됩니다.

아래는 몇 가지 예시입니다.

go
new(int) // int 포인터
new(string) // string 포인터
new([]int) // 정수 슬라이스 포인터
make([]int, 10, 100) // 길이 10, 용량 100 의 정수 슬라이스
make(map[string]int, 10) // 용량 10 의 맵
make(chan int, 10) // 버퍼 크기 10 의 채널

Golang by www.golangdev.cn edit