# 개념 #
* defer
- 정리 작업을 수행하는 함수 지정
- 현재 코드 블록이 끝날 때 자동으로 실행
- defer는 여러 번 사용될 수 있음 -> 나중에 defer 된 함수가 먼저 실행됨(LIFO)
- defer가 적용된 함수 중 반환 값이 있는 함수의 반환 값은 따로 읽을 수 없음
- defer와 이름 지정된 반환 값
* 값에 의한 호출(Call by Value)을 사용하는 Go
- Call by Value : 인자나 반환 값이 복사됨
- 원본이 변경되지 않음
- 데이터 보호 측면에서 유용
- 대량의 데이터를 처리할 경우 메모리와 시간이 많이 필요
cf.) Call by Reference : 주소가 복사됨
- 대용량 데이터의 전송이나 수정 시 주소만 넘겨주면 원본을 수정할 수 있어 복사가 필요 없음
- 원본이 변경될 우려 존재
- 기본 타입, 구조체는 call by value
- 맵
- 파라미터로 넘어온 맵에 대한 변경은 원본에도 적용
- 슬라이스
- 길이 조정 외의 모든 조작이 원본에도 적용
- 맵과 슬라이스는 포인터로 구현이 되었기 때문에 원본도 변경되는 것
* 포인터
- 값이 저장된 메모리의 위치 값(주소)을 저장하는 변수
- 주소 연산자(&)
- 간접 연산자(*, dereferencing operator)
- int32는 4바이트
- bool은 1바이트
- 모든 포인터는 어떤 타입을 가리키는가에 무관하게 항상 같은 크기
- 포인터의 제로 값 : nil
- 슬라이스, 맵, 함수, 채널, 인터페이스의 제로 값 : nil
- nil은 숫자 0이 아님
- nil을 숫자로 변경/변환하거나 숫자를 반대로 변환할 수는 없음
- 포인터 dereferencing 전에 nil인지 확인할 필요 있음
- 포인터 타입
- 포인터가 가리키는 값의 타입
- 포인터가 어떤 타입의 값을 가리키는지 나타냄
- 포인터 변수를 선언할 때 주로 사용
- 타입 이름 앞에 *
- cf.) 포인터 역참조 : 변수 이름 앞에 * (변수 이름 앞에 *는 포인터 역참조)
- var pointerToX *int -> pointerToX라는 변수는 int포인터 타입 (타입 이름 앞에 *는 포인터 타입)
- pointerToX = &x -> x는 int를 가리키는 변수인데 이 변수의 주소를 구해서 poiterToX에 저장함
- 내장 함수 new( )
- 포인터 변수 생성
- 해당 타입의 제로 값을 가리키는 포인터 반환
- 기본 타입의 리터럴이나 상수는 주소가 없으므로 주소 연산자를 사용할 수 없음
- 헬퍼 함수를 사용하여 주소 연산자를 사용할 수 없는 문제 해결
- 포인터는 변경 가능한 파라미터를 가리킴
* Java의 데이터 타입
- 원시 타입(Primitive Type) : 8개
- byte, short, int, long, float, double, bool, char : 값 그 자체, 객체가 아님, call by value
- 클래스 : 객체를 참조 변수로 참조해서 작업, call by reference
- 원시 타입의 클래스 : Byte, Short, Integer, Long, Float, Double, Boolean, Character
* Go 변수를 1) call by value로 할지 2) call by reference로 할지 선택 가능
- 1) 함수에 변수를 그냥 넘기는 것
- 2) 함수에 변수의 포인터를 넘기는 것
* 포인터는 변경 가능한 파라미터를 가리킴
- 비 포인터 타입 : 기본 타입, 배열, 구조체 => 원본의 불변성
- 포인터 : 함수로 전달되면 포인터의 복사본이 생성
- 원본에 닿을 수 있음
- 수정할 수 있음
* 포인터는 최후의 수단
- 포인터
- 데이터 흐름 이해를 어렵게 함
- GC에게 추가 작업 부하를 걸게 됨 (포인터를 이용하면 주로 힙을 이용)
: 가비지는 힙에 생성
- 포인터는 모든 데이터 타입을 함수로 전달할 때 상수 시간이 걸림 O(n)
- 주소를 함수에 전달하므로, 모든 데이터 타입의 주소 길이는 같음
- 대략 1메가 바이트 전달 기준으로
- 이상이면 포인터 전달
- 이하면 값 전달
* 제로 값과 값없음의 차이
- 0, ' ' vs NULL(nil), ''
값있음 vs 값없음
* 포인터를 이용하여 변수나 구조체 항목의 값이 제로 값인지 없는 값인지 구분
- 할당되지 않은 변수나 구조체 항목에 nil 포인터를 사용
- 포인터는 변경 가능함을 나타내므로 함수에서 nil 포인터를 직접 반환하는 것보다 콤마 OK 관용구를 사용하자
* nil 포인터를 함수의 파라미터나 구조체 항목의 값으로 담아서 함수의 인자로 넘기면 nil 포인터를 통해서는 값을 저장할 수 없으므로 함수 안에서 값을 설정(간접 연산자 *를 사용하는 것)할 수가 없음
* 버퍼 슬라이스
- 메모리 용도에 따른 구분
- 버퍼(buffer)
- 캐시(cache)
- 풀(pool)
* 가비지 컬렉션 작업량 줄이기
- 버퍼를 이용하면 GC의 부하를 줄일 수 있음
- 자동으로 메모리를 회수해 줌
- 가비지(Garbage)
- 더 이상 어떤 포인터도 가리키지 않는 데이터
* Go의 타입
- 내장 타입
- 기본 타입, 복합 타입
- 구조체를 이용한 사용자 정의 타입
- 구체적인 타입(구체 타입, concrete type)
cf.) abstract type
: abstract = general, super, 미완성(상속)
<-> concrete = specific, sub, 완성(상속)
* 매서드
- 타입(type)을 위한 메서드
- 반드시 패키지 블록 레벨에서 정의해야 함
- 리시버(receiver)
- func 키워드와 메서드 이름 사이에 리시버_이름 타입을 괄호로 감싸서 정의
- 리시버 식별자는 관례적으로 타입 이름의 짧은 약어인 첫 문자를 사용
* 포인터 리시버와 값 리시버
- 결정하는 규칙
- 메서드가 리시버를 수정 => 반드시 포인터 리시버 사용
- 메서드가 nil 인스턴스를 처리할 필요 => 반드시 포인터 리시버 사용
- 메서드가 리시버를 수정하지 않음 => 값 리시버 사용 가능
- 타입에 선언된 다른 메서드에 따라 결정
- 같은 타입의 다른 리시버가 포인터 리시버? : 리시버를 수정하지 않는 메서드라도 일관성을 위해 포인터 리시버 사용
# 실습 #
* int 타입을 가리키는 포인터 변수 x
* 포인터의 제로 값은 nil이므로 true 출력됨
* 둘 다 int타입 출력
* 내장 함수 new는 포인터 타입의 변수를 생성함 -> y라는 포인터 타입의 변수 생성
* 포인터 타입의 변수에 *를 붙이는 dereferencing(역참조)는 포인터 타입의 변수 y가 가리키는 값 0을 반환함
* fmt.Println(x) 하면 nil이 출력됨, fmt.Println(y)하면 주소 값이 출력됨
* 주어진 타입인 int의 제로 값을 가리키는 포인터 변수 y
* 포인터의 제로 값은 nil이지만 int 타입의 제로 값은 0
* 따라서, y == nil의 결과 false가 출력됨
'KOSA 클라우드 솔루션즈 아키텍트 양성과정' 카테고리의 다른 글
[5.23] 오픈스택 총정리(설치, 설정 등) (0) | 2022.05.23 |
---|---|
[5.20] Go 5일차(복습) (0) | 2022.05.20 |
[5.18] Go 3일차 (0) | 2022.05.18 |
[5.17] Go 2일차 (0) | 2022.05.17 |
[5.16] Go 1일차 (0) | 2022.05.16 |