[GoLang] 포인터

Pyo·2023년 11월 5일
0


이 글은 Tucker의 Go언어 프로그래밍 책을 14장 포인터 공부후 정리한내용이다.

포인터

포인터는 메모리 주소를 값으로 갖는 타입이다. 포인터를 이용하면 동일한 메모리 공간을 여러 변수가 가리킬수 있다.

포인터란

포인터는 메모리 주소를 값으로 갖는 타입이다.

  • 포인터 변수 선언
var a int
var p *int
p = &a
  • 포인터 변숫값 비교하기
package main

import "fmt"

func main() {
	var a int = 10
	var b int = 20

	var p1 *int = &a
	var p2 *int = &a
	var p3 *int = &b

	fmt.Printf("p1 == p2 : %v\n", p1 == p2)	// true
	fmt.Printf("p2 == p3 : %v\n", p2 == p3) // false
}

p1,p2,p3는 메모리의 주솟값을 가리킨다.

  • 포인터의 기본값 nil : 포인터 변숫값을 초기화 하지 않으면 기본값은 nil이다.
var p *int

if p! =nil{
	// p가 nil이 아니라는 얘기 : 유효 메모리 주소를 가리킨다.
}

포인터는 왜 쓰나?

변수 대입이나 함수 인수 전달은 값을 복사하기 때문에 많은 메모리 공간을 사용하는 문제와 큰 메모리 공간 복사시 성능 문제를 갖는다. 또한 다른 공간으로 복사되기 때문에 변경사항이 적용 되지 않는다.

  • 포인터를 사용하지 않았을때
package main

import "fmt"

type Data struct {
	value int
	data  [200]int
}

func ChangeData(arg Data) {
	arg.value = 999
	arg.data[100] = 999
}

func main() {
	var data Data

	ChangeData(data)
	fmt.Printf("value = %d\n", data.value)	// value = 0
	fmt.Printf("data[100] = %d\n", data.data[100])	// data[100] = 0
}

ChangeData() 함수 매개변수로 data 변숫값이 모두 복사되기 때문에 arg와 data는 서로 다른 메모리 공간을 갖는 변수이다. 따라서 arg값을 변경해도 data값은 변경되지 않는다. 또한 Data 구조체는 int 타입 value와 크기가 200인 int 타입 data로 구성되어 총 1608바이트가 복사 된다.

  • 포인터를 사용했을때
package main

import "fmt"

type Data struct {
	value int
	data  [200]int
}

func ChnageData(arg *Data) {
	arg.value = 999
	arg.data[100] = 999
}

func main() {
	var data Data

    ChnageData(&data)
	fmt.Printf("value : %d\n", data.value)
	fmt.Printf("data : %d\n", data.data[100])
}

ChageData() 함수 매개변수로 Data 구조체의 포인터 즉 주소값을 받는다. arg 포인터 변수가 가리키는 구조체의 값을 변경하면 data 구조체 주솟값이기 때문에 data의 값도 변경된다. 또한 구조체 전부가 복사되는 것이 아닌 8바이트만 복사된다.

  • Data 구조체를 생성해 포인터 변수 초기화하기
// 	기존 방식
var data Data 
var p *Data = &data

// 구조체를 생성해 초기화하는 방식
var p *Data = &Data{}

인스턴스

메모리에 할당된 데이터의 실체를 말한다.

var p1 *Data = &Data{}
var p2 *Data = p1
var p3 *Data = p1

실제로 메모리에 할당된 데이터는 한개이기때문에 인스턴스는 한개이다.

var data1 Data 
var data2 Data = data1
var data3 Data = data1

메모리에 값이 세개가 할당 되었기 때문에 인스턴스는 3개가 된다.

  • 인스턴스는 데이터의 실체다
  • new() 내장함수 : new를 사용할 경우 필드값을 초기화 할수는 없다.
p1 := &Data{}		// &를 사용하는 초기화
var p2 = new(Data) 	// new()를 사용하는 초기화
  • 인스턴스는 언제 사라지나 : Go언어는 쓸모없는 데이터를 메모리에서 해제하는 가비지 컬렉션 기능을 제공한다.

스택 메모리와 힙메모리

대부분의 언어는 이론상 스택 메모리영역에 메모리를 할당하는것이 효율적이다. 하지만 스택 메모리는 함수 내부에서만 사용 가능한 영역이다. Go언어는 탈출 검사를 통해 메모리 공간이 함수 외부로 공개되는지 여부를 자동으로 검사해서 스택 메모리에 할당항지 힙 메모리에 할당할지 결정한다.

정리

  • 포인터는 메모리 주소를 값으로 갖는 타입이다.
  • &를 이용해서 데이터 메모리 주소를 알수 있다.
  • 포인터를 이용하면 메모리 주솟값으로 메모리를 조작할 수 있다.
  • 인스턴스는 메모리에 있는 데이터 실체이고 포인터로 조작할 수 있다.
  • Go는 탈출 분석을 통해 인스턴스를 스택 메모리에 할당할지 힙 메모리에 할당할지 결정한다.

참고

https://www.youtube.com/@TuckerProgramming

0개의 댓글