더북(TheBook)

3.6.2 값 전달

Go에서는 함수나 메서드를 호출할 때 매개변수 값을 복사해서 함수나 메서드 내부로 전달한다. 그래서 함수나 메서드 내부에서는 전달된 매개변수의 원본 값을 변경할 수 없다. 함수나 메서드 내에서 원본 값을 변경하려면 포인터를 사용해야 한다.

포인터 값의 크기는 64비트 머신에서는 8바이트, 32비트 머신에서는 4바이트이다. 함수나 메서드로 포인터를 전달하면 실제 포인터가 가리키고 있는 값에 상관없이 아주 적은 양의 값만 전달된다.

불 타입이나 숫자 타입 값은 크기가 1바이트에서 8바이트 정도이므로 함수나 메서드 내부로 값을 전달할 때 값을 복사하는 작업이 시스템에 부담을 주지 않는다. 문자열을 전달하는 것도 가볍게 동작한다. Go 런타임은 문자열을 전달할 때 실제 그 값이 얼마나 큰지에 상관없이 데이터를 아주 적은 양만 전달하도록 최적화되어 있기 때문이다(문자열 하나를 함수나 메서드에 전달할 때 실제 전달되는 값은 64비트 머신에서는 16바이트, 32비트 머신에서는 8바이트이다). 물론 += 같은 작업을 할 때는 문자열 전체를 복사한다.

C나 C++와 달리 Go의 배열은 값을 복사해서 함수나 메서드 내부로 전달한다. 그래서 큰 배열을 함수나 메서드로 전달하면 시스템에 부담이 많이 된다. 길이가 큰 배열을 함수나 메서드로 전달할 때는 배열 대신 슬라이스를 사용하는 것이 좋다. 함수나 메서드로 슬라이스를 전달하면 참조가 전달되므로 시스템에 덜 부담된다.

func main() {
    // 함수에 슬라이스를 전달하면 시스템의 부담을 줄일 수 있음
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    multiply(numbers, 5)
    fmt.Println(numbers)
}
func multiply(numbers []int, factor int) {
    for i := range numbers {
        numbers[i] *= factor
    }
}

크기가 큰 구조체를 함수나 메서드에 전달할 때는 값 전체를 복사해서 전달하므로 시스템에 부담을 많이 준다. 이럴 는 구조체 값을 직접 전달하지 않고 포인터로 전달하면 시스템에 주는 부담을 줄일 수 있다.

type rect struct {
    x0, y0, x1, y1 int
    color.RGBA
}
 
func main() {
    rect := rect{2, 4, 10, 20, color.RGBA{0xFF, 0, 0, 0xFF}}
    resize(&rect, 10, 10)
    fmt.Println(rect)
}
 
func resize(rect *rect, width, height int) {
    (*rect).x1 += width
    rect.y1 += height
}

함수나 메서드의 반환 값이 많을 때는 슬라이스를 반환하거나, 반환 값 여러 개를 구조체로 정의하여 구조체의 포인터를 반환하는 것이 좋다. 함수나 메서드에서 포인터나 참조가 아닌 값을 반환할 때 반환 값은 세 개 이하로 정의하는 것을 권장한다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.