[GoLang] 함수 parameter는 어떻게 전달될까?

타미·2021년 2월 1일
2

Hello Golang

목록 보기
3/7
post-thumbnail
post-custom-banner

GoLang 뉴비로 엄청 했갈렸던 부분이 있다.
함수 parameter로 어떤 데이터를 넘기고, 함수 내에서 변경이 생겼을 때 어쩔 때는 변경이 반영되고 어쩔 때는 변경이 반영되지 않는다는 것이다! 찾아보니 간단한 내용이었는데 처음 공부하는 사람은 헷갈리는 부분이라 정리해본다 :>


Call By Value

GoLang은 기본적으로 Call By Value를 지원한다.

JAVA와 달리, GoLang은 기본적으로 Call By Value를 지원한다.
Call By Reference를 하고 싶으면 포인터로 넘겨주는 방식이다.

// call by value
func change1 (num int) {
	num = 100
}

// call by reference
func change2 (num *int) {
	&num = 100
}

num := 1
change1(num) // num == 1, 변경 X
change2(&num) // num == 100, 변경 O

Value Type과 Reference Type

기본적으로 Call By Value 방식으로 동작하며, 이후 데이터가 Value Type인지 Reference Type인지에 따라 동작이 다르다.

  • Value Type
    • Stack 안에 실제 데이터가 있다.
    • struct int
      객체, 즉 struct가 Value Type인게 좀 헷갈린다.. Stack은 후입선출로 주로 함수를 호출했을 때 그 정보 같이 순서대로 소멸될 것을 넣을 것 같았는데, 왜 struct를 Value Type으로 만들었을까?
  • Reference Type
    • Stack (주소) → Heap (실제 데이터)
    • Map Slice string

Value Type이든 Reference Type이든,
Call By Value 방식이기 때문에 함수를 실행하면 해당 데이터를 복사하여 사용한다.


Value Type

type Student struct {
	name string
	age int
}

func Change1(student Student) {
	student.age += 10
}

func Change2(student *Student)  {
	(*student).age += 10
}

student := Student{"Tommy", 26}
Change1(student) // 결과값: Tommy, 26
Change2(student) // 결과값: Tommy, 36


Change1


함수를 호출한 main 함수

Change1 (Call By Value)는 Student 내부 데이터를 복사해왔지만, Value Type이기 때문에 원본 데이터와 다른 주소값에 존재한다. -> 실질적으로 원본 데이터에 영향을 주지 못한다.
Change2의 경우 원본 데이터의 주소를 그대로 가지기 때문에 데이터를 바꿀 수 있다.

포인터 리시버

func (w *Wallet) DepositWithValueReceiver(amount int) {
	w.balance += amount
}

// Production add: 0xc000016378, value: 0
// Test add: 0xc000016378, value: 10
  • struct 타입만! 포인터 리시버가 자동 역참조해준다.
  • 내부적으로 (*w).balance += amount 와 같다.

Reference Type

Reference Type도 Call By Value이기 때문에 Value Type과 마찬가지로 parameter로 전달한 데이터만 복사한다. 다만 Reference Type 자체가 주소로 변경되기에 함수에서의 변경이 영향이 가는 것이다.

Slice를 통해 좀 더 자세히 알아보자

Slice의 내부 구조

Slice의 구성요소

  • Ptr
    • Slice 내부 값
    • 실제로는 array와 동일
  • len
  • cap

요기 3개는 stack에 있고, 데이터가 있는 array는 heap영역에 존재한다.

func Change1(numbers []int) {
	numbers[0] = 1000 // 주소: 0xc00004e6e8
}

numbers := make([]int, 10)
numbers[0] = 1 // 주소: 0xc00004e6e8
Change1(numbers) // numbers[0] = 1000으로 변경된다.

Change1 함수에 갔을 때 있는 것은 numbers의 Ptr, len, cap이다.
이 중 ptr로 array에 접근하기 때문에 변경이 반영된다.

그렇다면 다음 함수의 결과는 어떻게 될까?

func Change1(numbers []int) {
	numbers = append(numbers, 1000)
}

numbers := make([]int, 3)
numbers[0] = 1 // {1,0,0}
Change1(numbers)

정답은 {1,0,0} 그대로이다.
append하여 array 내부적으로는 {1,0,0,1000}이 되지만 numbers의 len,cap 때문에 4번째 데이터를 볼 수 없는 것이다.
slice는 append할 때 len,cap 변수를 조정해줘야 하기 때문에 포인터 변수로 넘겨야 한다!

profile
IT's 호기심 천국
post-custom-banner

0개의 댓글