본문으로 바로가기

pass by value, pass by reference

 

함수의 매개변수로 값 자체를 전달하는 방식(Pass by value, 값전달)과 값의 주소를 전달하는 방식(Pass by reference, 레퍼런스전달)이 있다. Go는 무조건 pass by value다. (값전달) 그러나 포인터를 파라미터로 넣어서 pass by reference처럼 사용할 수 있다.

* Go에서는 딱히 pass by reference나 pass by pointer를 구분하지는 않는다.

 

Pass by value는 인자의 값을 복사해서 전달하는 방식입니다. 

func addThree(a int) {
	a += 3
	fmt.Println(a)
}

func main() {
	a := 4 

	addThree(a) // 7 출력

	fmt.Println(a) // addThree 함수가 스택 메모리에서 pop 되면서 사라지고, 지역 변수인 a = 4 출력
}

 

pass by reference는 인자의 값이 아닌 인자의 값이 저장된 메모리 주소를 넘깁니다.

아래 코드를 보시면 파라미터가 *int (주소값, 포인터) 입니다. 메모리 값에 조작이 이루어졌으므로 해당 a는 addThree 함수가 끝나더라도 4가 아닌 7을 출력합니다.

package main

import "fmt"

func addThree(a *int) {
	*a += 3
	fmt.Println(*a)
}

func main() {
	a := 4 //지역변수 선언

	addThree(&a) //참조를 위한 a의 주솟값을 매개변수로 전달, 7 출력

	fmt.Println(a) // 4가 출력될 것으로 예상했으나 메모리 주소에 직접 조작. 4가 아닌 7 출력
}

 

 

가변 인자 함수 Variadic parameter ...

python에서 *args, **kwargs를 사용해보신 경험이 있으실 겁니다. 그겁니다.

 

  • n개의 동일한 형의 매개변수를 전달합니다.
  • 전달된 변수들은 슬라이스 형태입니다. 변수를 다룰 때 슬라이스를 다루는 방법과 동일합니다.
  • 함수의 '매개변수형' 앞에 '...'을 붙이면 됩니다. ....은 
  • 매개변수로 슬라이스를 전달할 수 있습니다. 다른 컬렉션 형태는 불가능합니다. 슬라이스를 전달할 때는 슬라이스 이름 뒤에 ...를 붙여서 "함수이름(슬라이스이름...)" 형식으로 함수를 호출하면 됩니다. unfurling a slice!
package main

import "fmt"

func addEvery(num ...int) int {
	var result int

	// num은 슬라이스입니다
	for _, val := range num {
		result += val
	}

	return result
}

func main() {
	num1, num2, num3, num4, num5 := 1, 2, 3, 4, 5
	nums := []int{10, 20, 30, 40}

	fmt.Println(addEvery(num1, num2, num3, num4, num5)) // 15
	fmt.Println(addEvery(nums...)) // 슬라이스 풀어 헤치기
}

 

 

위와 같이 사용하기 싫다면 그냥 아래처럼 인자를 []int 꼴로 넘기시면 됩니다. 무엇을 사용하던 취향껏.

package main

import "fmt"

func main() {
	s := []int{1, 2, 3, 4, 5, 6}
	fmt.Println(foo(s...))
	fmt.Println(bar(s))
}

func foo(x ...int) int {
	sum := 0
	for _, v := range x {
		sum += v
	}
	return sum
}

func bar(y []int) int {
	sum := 0
	for _, v := range y {
		sum += v
	}
	return sum
}

 

특정 타입이 아닌 여러 타입 인수를 섞어 쓰고 싶다면 empty interface를 가변 인자로 넣으면 됩니다.

그리고 여러 타입이 존재한느 경우 함수 내에서는 switch문을 이용하여 타입에 따라 다른 처리를 하는 것이 일반적입니다. 아래처럼요.

https://tour.golang.org/methods/16

func variadic(args ...interface{}) {
	for _, v := range args {
		switch f := v.(type) {
		case bool:
			val := v.(bool)
			fmt.Println("this is bool", val)
		case int:
			val := v.(int)
			fmt.Println("this is int", val)
		case string:
			val := v.(string)
			fmt.Println("this is string", val)
		case float64:
			val := v.(float64)
			fmt.Println("this is float64", val)
		default:
			fmt.Println("what the?", f)
		}
	}
}

 

 

Multiple returns

 

이상하게 들리겠지만 여러 값을 return할 수 있다. JS처럼 한 객체에 넣어준다던지 그런 거 없어도 된다.

그리고 괄호 없이 int, string 이렇게 연달가 적어주면 에러가 난다. 반환은 (int, string) 과 같은 꼴로!

package main

import (
	"fmt"
	"strings"
)

func lenAndUpper(name string) (int, string) {
	return len(name), strings.ToUpper(name)
}

func main() {
	// 일종의 구조분해 할당
	totalLen, upperName := lenAndUpper("Martin")
	fmt.Println(totalLen, upperName)
}

 

naked return

무엇을 리턴할 것인지 정의해두었다면 return으로 명시하지 않더라도 자동으로 return합니다.

아래의 경우 length, uppercase라는 변수명으로 리턴하라고 하였으므로 return에 따로 명시하지 않아도 자동으로 return됩니다.

 

사실 return을 뭘 할지 직접 explicit하게 하는 것이 여러 사람들에게 일반적으로 좋다고 생각하므로 잘 사용되는 편은 아니라고는 합니다. 사용 안하는 편이 좋을 것 같네요.

package main

import (
	"fmt"
	"strings"
)

func lenAndUpper(name string) (length int, uppercase string) {
	length = len(name)
	uppercase = strings.ToUpper(name)
	return
}

func main() {
	totalLen, upperString := lenAndUpper("Martin")
	fmt.Println(totalLen, upperString)
}

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체