{Go} 8. pointer / 9. function / 10. clousure / 11. defer

Jerry·2021년 6월 19일
0

8. pointer

  • pointer를 이용하여 자바, 파이썬, C# 에서 불가한 메모리 직접 참조를 가능하게 함
  • Go에서 주소 값을 직접 변경은 불가능하다.
    => 잘못된 주소값 조작으로인한 오류를 방지하기위해
  • *(에스터리스크) 사용
  • nil로 초기화

선언방법

// 방법 1.
var a *int 
// 방법 2.
var b *int = new(int)

포인터 기본 사용

p := 7

fmt.Println(p) // p값
fmt.Println(&p) // p의 주소

a := &p

var b *int
b=&p

fmt.Println(a,b) // p의 주소
fmt.Println(&a,&b) // a와 b의 주소
fmt.Println(*a,*b) // p의 값 : 역참조

*a++ // a가 역참조한 (*a) 값을 증가 시킴. 7 출력

포인터 함수 인자 전달 사용


func rptc(n *int) {
	*n = 77
}

var a int = 10
fmt.Println(a) // 10 출력
rptc(&a)
fmt.Println(a) // 77 출력

9. function

함수 선언

func 함수이름 (입력파라미터)(리턴파라미터)

  • 함수 이름 첫글자가 소문자면 private 접근제어로 외부 참조 불가
  • 함수 이름 첫글자가 대분자면 public 접근제어로 외부 참조 가능
  • 리턴 파라미터를 여러 개 반환 할 수 있는 게 특징 (생략도 가능)
func multiply(x int, y int) (int, int) { //(x,y int 가능)
	return x * 5, y * 5
}

func arrayMultiply(a, b, c, d, e int) (int, int, int, int, int) {
	return a * 1, b * 2, c * 3, d * 4, e * 5 // 리턴 5개
}

// 리턴 파라미터를 받을 변수를 리터럴로 선언. 불필요한 값은 밑줄(_)로 생략
a, b := multiply(1, 2)
c, _ := multiply(5, 2)
_, d := multiply(1, 10)

x1, x2, x3, x4, x5 := arrayMultiply(1, 1, 1, 1, 1)
x6, _, x7, _, x10 := arrayMultiply(1, 1, 1, 1, 1)
  • 입력 파라미터 수 가변 가능

func sum(n ...int) int {
	tot := 0
	for _, value := range n {
		tot += value
	}
	return tot
}

func prtWord(msg ...string) {
	for _, value := range msg {
		fmt.Println(value)
	}
}

y := sum(1, 2, 3, 4, 5, 6, 7)
prtWord("a", "apple", "gogogo")

// 슬라이스 넘기기
a := []int{5, 6, 6, 7, 8, 9, 10}
m := sum(a...)

함수배열

//함수를 배열에 할당

func sum(n ...int) int {
	tot := 0
	for _, value := range n {
		tot += value
	}
	return tot
}

func multiply(x, y int) (r int) {
	r = x * y
	return r
}

f := []func(int, int) int{multiply, sum}

a := f[0](10, 10)
b := f[1](10, 10)


// 함수를 변수에 할당
var f1 func(int, int) int = multiply
f2 := sum

a := f1(10, 10)
b := f2(10, 10)

// 함수를 맵에 할당
m := map[string]func(int, int) int{
	"mul_func": multiply,
	"sum_func": sum,
}

fmt.Println(m["mul_func"](10, 10))

익명함수

  • 선언과 동시에 즉시 실행
func main() {
      func() {
          fmt.Println("즉시 실행")
      }()
  }

  msg := "helelo golang"
  func(m string) {
      fmt.Println(m) // msg 문자열 값 즉시 실행
  }(msg)

  // 즉시 실행하여 변수에 할당
  r := func(x, y int) int {
      return x * y
  }(10, 100)    
}

10. closure : 클로저

  • 익명함수 선언으로 구현
  • 함수안 함수 선언 시 외부 변수를 참조하는 경우 외부 변수에 대한 접근을 유지
    => 이 참조되는 외부 변수를 자유 변수 (free variable) 이라고 함

예시1) 외부 변수를 참조하여 실행

m, n := 5, 10           
sum := func(c int) int { // 익명함수 변수 할당
    return m + n + c // m과 n은 소멸되지 않음
}
r2 := sum(10)

예시 2) 함수 내 함수 리턴

func main() {
	cnt := increaseCnt()
	fmt.Println(cnt())
    fmt.Println(cnt())
}

func increaseCnt() func() int {
	n := 0 
	return func() int {
		n += 1  // 리턴되는 함수에서 n이 사용되므로 리턴된 함수에 대한 참조가 사라질때 까지 n은 메모리에서 반환 대상이 안됨
		return n
	}
}
  • 전역변수를 쓰지 않고 클로저를 사용하는 이유?
    => 전역변수는 다른 함수, 모듈에서도 참조 가능하므로 개발자의 실수로 값이 잘못되는 상황이 발생할 수 있지만 클로저는 해당 함수 내에서만 기억하는 것이므로 외부에서는 참조하지 못하게 하면서 값을 기억할수 있는 것이 장점

  • 클로저는 함수 외부 변수에 대한 참조를 유지 하기 때문에 무분별한 클로저 남발은 메모리 누수로 이어질 수 있음


var closurFunc func(int) int

func outerFunc() {
	m, n := 5, 10
	closurFunc = func(c int) int { // 익명함수 변수 할당
		return m + n + c 
        // outerFunc가 종료되어 메모리가 반환되어도 m, n 지역변수는 참조가 살아있어 GC에서 메모리 해제가 되지 않음
	}
	r2 := closurFunc(10)
	fmt.Println("here ", r2)
}

func main(){
	outerFunc()
    	// outerFunc의 지역 변수인 m,n에 대한 메모리가 해제되지 않으므로 outerFunc이 반환되어도 closurFunc 사용 가능. 
    	// 이런식의 코딩이 많아져 지역변수들중 전역변수화 되어 메모리 해제가 되지 않는게 많아지면 GC에서 정리되지 않아 메모리 부족 현상이 발생할수있음 
	r2 := closurFunc(20)
	fmt.Println("here ", r2)
}

11. defer

함수 실행 지연 예약어

  • defer를 호출한 함수가 종료되지 직전에 호출된다.
  • defer가 여러개 선언되면 스택처럼 먼저 예약된게 나중에 실행됨
func ex_f1() {
	fmt.Println("f1 : start! ") // 출력 순서 1
	defer ex_f2() // 출력 순서 4
	fmt.Println("f1 : end") // 출력 순서 2
    defer func(){
    	fmt.Println("third") // 출력 순서 3
    }()
}

func ex_f2() {
	fmt.Println("f2 : called")
}

func main() {
	ex_f1()
}
  • 타 언어의 finally와 비슷
  • 주 사용처 : 리스소 반환(ex. 파일닫기, 소켓닫기), 락 해제 등

중첩 함수에 defer 사용한 경우, 실행순서 유의

func start(t string) string {
	fmt.Println(" start: ", t) // 실행 1
	return t
}
func end(t string) {
	fmt.Println(" end : ", t)  // 실행 3
}

func a() {
	defer end(start("b")) //중첩함수 주의. start 함수는 먼저 실행됨.
	fmt.Println("in a")  // 실행 2
}

func main() {
	a()
}

참고자료

[학습 자료] 인프런 - 쉽고 빠르게 끝내는 GO언어 프로그래밍 핵심 기초 입문 과정
[공식사이트] https://golang.org/tutorial

profile
제리하이웨이

0개의 댓글