14.1. 프로그램언어 고(Go)에서의 포인터 사용법

프로그램언어 고(Go)의 기본적인 포인터 사용법

고(Go)언어에서 포인터는 메모리 주소를 가리키는 변수입니다. 포인터를 사용하면 메모리 주소에 직접 접근할 수 있어 효율적인 메모리 접근과 수정이 가능합니다.


package main

import "fmt"

func main() {
  var a int = 10    // 정수형 변수 a를 선언하고 10을 저장
  
  var b *int = &a   // a 변수의 메모리 주소를 저장하는 포인터 b를 선언 

  fmt.Println(&a)   // a의 메모리 주소 출력
  fmt.Println(b)    // b가 가리키는 a 변수의 메모리 주소 출력  

  *b = 20           // b가 가리키는 a 변수의 값을 20으로 변경
  fmt.Println(a)    // 변경된 a 변수의 값 20 출력
}

위 예제에서 변수 a는 정수 10을 저장하고 있습니다. 변수 b는 a의 메모리 주소를 가리키는 포인터 변수입니다.

&a는 a의 메모리 주소를 나타내고, b도 a의 메모리 주소와 동일합니다.

*b = 20은 b가 가리키는 메모리 주소에 20을 저장하는 것으로, 결국 a 변수의 값이 20으로 변경됩니다.

즉, 포인터b를 이용해 a변수에 간접적으로 접근해 값을 변경한 것입니다.


package main

import "fmt"

func change(num *int) {
  *num = 55   // 함수 입력으로 들어온 num 포인터가 가리키는 값을 55로 변경 
}

func main() {
  
  a := 10
  
  fmt.Println("before:", a) // 10
  
  change(&a)  
  
  fmt.Println("after:", a) // 55
}  

위 예제는 함수의 입력 매개변수로 포인터를 넘겨서 값을 변경하는 예입니다.

change 함수가 호출될 때, main 함수의 변수 a의 메모리 주소인 &a를 넘깁니다.

change 함수 내에서는 입력받은 num 포인터를 이용해 그가 가리키는 메모리의 값을 변경합니다.

main 함수로 돌아와서 출력하면 변경된 a 변수의 값 55가 출력됩니다.

이처럼 포인터를 사용하면 함수 외부에 선언된 변수에 접근할 수 있습니다.

고맙습니다. 포인터에 대한 설명이 부족하다면 언제든지 문의주세요.

프로그램언어 고(Go)에서의 포인터로 배열 조작하기

프로그램언어 고(Go)에서 배열을 포인터로 조작할 수 있습니다. 배열은 기본적으로 포인터이기 때문에 배열을 가리키는 포인터를 만들어 연산을 수행할 수 있습니다.

예를 들어,

package main

import "fmt"

func main() {
    arr := [3]int{1, 2, 3} // 배열 선언

    var ptr *[3]int = &arr // 배열을 가리키는 포인터

    fmt.Println((*ptr)[0]) // 1: 첫 번째 원소 출력

    (*ptr)[1] = 5 // 두 번째 원소 값 변경
    fmt.Println(arr[1]) // 5: 변경된 값 출력
}

위의 코드에서 arr 배열을 가리키는 ptr 포인터를 선언했습니다.
이 ptr을 역참조 연산자(*)를 사용해 배열의 원소에 접근할 수 있습니다.

또한 ptr을 사용해 배열의 원소를 변경할 수도 있습니다.
ptr을 수정하면 실제 arr 배열의 값도 변경됩니다.

이처럼 Go에서는 배열 자체가 주소값을 갖기 때문에, 배열을 가리키는 포인터를 활용해 배열을 조작할 수 있습니다.
포인터를 사용하면 배열을 함수 등에 전달할 때 효율적으로 전달할 수 있다는 장점이 있습니다.

프로그램언어 고(Go)에서의 포인터와 슬라이스 활용법

고(Go)언어에서 포인터와 슬라이스는 중요한 개념입니다.

package main

import "fmt"

func main() {
  
  var a int = 10
  
  var b *int = &a
  
  fmt.Println(a, b)  

  fmt.Println(*b)

}

위의 예제에서 보시다시피, 포인터 변수 b는 a 변수의 메모리 주소를 저장합니다. 여기서 & 기호는 주소 연산자로, 변수의 메모리 주소를 반환합니다.

*b를 출력하면 역참조 연산자로 실제 그 메모리 주소의 값인 10을 출력합니다.

즉, 포인터는 기존 변수의 메모리 주소를 저장하는 변수로, 역참조 연산자(*)를 사용하여 그 주소의 실제 값에 접근할 수 있습니다.

package main  

import "fmt"

func main() {

 var numbers = []int{1,2,3} 

 var slice []int = numbers[0:2]

 fmt.Println(numbers, slice)

}

슬라이스는 기존 배열이나 슬라이스에서 연속된 부분 집합을 공유하는 것입니다. 위 예제의 numbers는 배열인데 slice는 이 배열의 인덱스 0부터 2 바로 앞까지의 부분 집합을 공유합니다.

즉, 슬라이스는 배열의 포인터와 길이를 사용하여 배열의 일부를 참조하는 것으로 파악할 수 있습니다. 실제 데이터를 복사하지 않고도 배열의 일부를 다룰 수 있기 때문에 유용합니다.

이상으로 고(Go)언어에서의 포인터와 슬라이스에 대한 간략한 설명을 드렸습니다. 예제와 함께 이해하기 쉽게 설명드렸기를 바랍니다. 추가로 궁금한 점이 있다면 문의 부탁드리겠습니다.

프로그램언어 고(Go)에서의 포인터 연산

고(Go)언어에서 포인터 연산은 C와 비슷하게 동작합니다.


package main

import "fmt"

func main() {
  var a int = 10
  
  // a의 메모리 주소 구하기  
  var b *int = &a
  
  // b가 가리키는 메모리 주소의 값 출력  
  fmt.Println(*b) // 10
  
  // b의 메모리 주소 값 출력
  fmt.Println(b)  // 0xc0000b4008  
  
  // b에 1을 더해서 다음 int 변수의 주소를 가리키게 함  
  b++
  
  // b가 가리키는 새로운 메모리 주소의 값 출력  
  // 쓰레기 값이 출력됨  
  fmt.Println(*b) // 의미없는 값  
  
  // b의 메모리 주소 값 출력  
  // a 메모리 주소보다 1 int 크기만큼 증가된 값 출력  
  fmt.Println(b)  // 0xc0000b400c
}  

위 예제코드에서 보면,
포인터 변수 b에 1을 더하면, b가 가리키는 메모리 주소가 int형 변수의 크기인 4바이트만큼 증가합니다.

따라서 b++ 연산 후에는 b가 a 변수의 메모리 주소에서 4바이트 떨어진 곳을 가리키게 되어, 그 메모리값을 출력하면 쓰레기값이 나오게 됩니다.

이와 같이 고(Go)언어의 포인터도 C언어와 마찬가지로 산술연산 가능합니다.
다만 보통 실제 코드에서는 이러한 포인터 연산을 잘 사용하지 않고, 대부분 추상화된 인터페이스와 라이브러리를 사용하게 됩니다.

포인터 연산은 참조받는 메모리 영역을 직접 제어할 수 있다는 장점이 있지만, 위험하고 복잡성이 높다는 단점도 있기 때문입니다.

따라서 고(Go)에서는 가능하긴 하지만, 일반적으로 권장하지 않는 사용법임을 알아두시기 바랍니다.

프로그램언어 고(Go)에서의 포인터를 사용한 메모리 관리

고(Go) 언어에서 포인터를 사용한 메모리 관리에 대해 설명드리겠습니다.

고(Go) 언어에서 변수를 선언하면 그 변수는 메모리 공간을 할당받습니다. 포인터 변수를 사용하면 해당 메모리 주소를 가리킬 수 있습니다. 예를 들어:

package main

import "fmt"

func main() {
    var a int = 10 
    var b *int = &a
    
    fmt.Println(a) 
    fmt.Println(b)  
    fmt.Println(*b) 
}

여기서 a 변수는 int 타입으로 메모리 공간을 할당받습니다. b 변수는 a 변수의 메모리 주소를 가리키는 포인터 변수입니다.

& 기호를 사용하여 a 변수의 주소를 b에 할당하였습니다. b를 통해 a 변수의 값에 접근할 수 있습니다. *b를 통해 역참조(dereferencing)하여 a 변수의 값 10을 출력할 수 있습니다.

이러한 포인터의 특성을 이용하여 메모리 주소 직접 관리, 대용량 데이터 처리, 코드 효율화 등을 할 수 있습니다. 예를 들어 배열이나 구조체에 대한 참조를 전달할 때 매우 유용합니다.

포인터 연산도 가능합니다. 포인터에 정수 값을 더하거나 뺄 수 있는데, 그 결과로 해당 타입의 크기만큼 떨어진 메모리 주소를 가리키는 새로운 포인터가 생성됩니다.

이외에 Go 언어의 가비지 컬렉션(Garbage Collection)도 메모리 관리 측면에서 중요합니다. 사용하지 않는 변수나 데이터는 자동으로 메모리에서 해제되기 때문에, 개발자가 직접 메모리를 관리할 필요가 크지 않습니다.

이상으로 Go언어의 포인터를 사용한 메모리 관리에 대해 간략히 설명드렸습니다. 추가적인 질문이 있다면 편하게 문의주시기 바랍니다.

Leave a Comment