[Golang] Struct와 Pointer

iguigu·2022년 4월 16일
0

Sturct (구조체)

Struct의 멤버로 Struct를 사용하는게 가능하다

package main

import "fmt"

type Student struct {
	Name  string
	Class int
	No    int
	Score float64
}

type VIPStudent struct {
	stud  Student
	isVIP bool
}

func main() {
	var student VIPStudent

	student.stud.Name = "홍길동"
	student.isVIP = true

	fmt.Println("이름 : ", student.stud.Name)
	fmt.Println("VIP : ", student.isVIP)

}

메모리 정렬을 통해 효율적으로 저장공간을 사용할 수 있게 해야한다


// Not Good, 56byte
type Padding struct{
	A int8		// 8byte (padding)
    B int		// 8byte
    C float64	// 8byte
    D uint16	// 8byte (padding)
    E int		// 8byte
    F float32	// 8byte (padding)
    G int8		// 8byte
}


// Good, 32byte
type Padding struct{
	A int8		// 1byte
    G int8		// 1byte
    D uint16	// 2byte
    F float32	// 4byte
    B int		// 8byte
    C float64	// 8byte
    E int		// 8byte

}

생성과 동시에 바로 사용할 수밖에 없는 익명 구조체

func main() {
	p1 := struct {
		first string
		last  string
	}{first: "James", last: "Bond"}
	fmt.Println(p1)
}

Marshaling

  • Marshaling은 데이터를 전송하거나 보관할 때 효율적인 형태로 변경하는 것을 의미
  • Go struct를 JSON 형태로 변환 또는 그 역이 가능

package main

import (
	"encoding/json"
	"fmt"
)

type Employee struct {
	Name   string `json:"empname"`
	Number int    `json:"empid"` // number와 같이 소문자로 작성 시 json/encoding에서 파싱되지 않아 무시 됨
}

func main() {
	employee_1 := &Employee{Name: "Dixya Lhyaho", Number: 10}
    
    // Marshal
	e, err := json.Marshal(employee_1) // MarshalIndent 사용 시 들여쓰기가 적용됨
	if err != nil {
		fmt.Println(err)
		return
	}
    fmt.Println(e) // [123 34 101 109 112 110 97 109 101 34 58 34 68 105 120 121 97 32 76 104 121 97 104 111 34 44 34 101 109 112 105 100 34 58 49 48 125]
	fmt.Println(string(e)) // {"empname":"Dixya Lhyaho","empid":10}
    
    // Unmarshal
	var emp Employee

	err2 := json.Unmarshal(e, &emp)
	if err2 != nil {
		fmt.Println(err2)
		return
	}
	fmt.Println(emp.Name) // 10
	fmt.Println(emp.Number) // Dixya Lhyaho
}

참고 사항. Unmarshal vs Decode

  • json.Umarshal
    - byte[]를 입력으로 가짐
    - 메모리를 효율적으로 사용 (Decode는 json 데이터를 메모리에 버퍼링 하여 비효율적임)
  • decoder.Decode
    - io.reader (file, HTTP request)를 입력으로 가질 때
    - json 데이터가 stream으로 들어올 때 용이함

Pointer (포인터)

  • 변수 대입과 함수 인수 전달은 항상 값을 복사하기 때문에 메모리 비효율과 성능 문제를 발생 시킴
  • 포인터는 이 부분을 효과적으로 해결함
package main

import "fmt"

type Data struct {
	value int
	data  [200]int
}

func ChangeData(arg *Data) {
	arg.value = 999
	arg.data[100] = 999
}

func main() {
	var data Data

	// 포인터로 전달하지 않으면 값이 변경되지 않음
	ChangeData(&data)
	fmt.Printf("value = %d\n", data.value)
	fmt.Printf("data[100] = %d\n", data.data[100])
}

구조체 변수를 생성하지 않고 곧바로 포인터 변수에 구조체 생성하여 초깃값 대입 가능

	// 구조체 변수 생성 및 초깃값 대입
    var data Data
    var p *Data = &data
    
    // 구조체 생성과 동시에 초깃값 대입
    var p *Data = &Data{}	// 방법 1
    var p = new(Data) 		// 방법 2

포인터를 복사하면 인스턴스가 생기는 것이 아님

// 인스턴스가 추가로 생성되는 것이 아님 (인스턴스 1개, 포인터가 동일하게 가르킴)
var p1 *Data = &Data{}
var p2 *Data = p1
var p3 *Data = p2

// 값만 동일하고 각각 개별적인 인스턴스가 생김 (인스턴스 3개)
var data1 Data
var data2 Data = data1
var data3 Data = data1

함수 외부로 공개되는 인스턴스의 경우 함수가 종료되어도 살아있음

package main

import "fmt"

type User struct{
	Name string
    Age int
}

func NewUse(name string, age int) *User{
	// 함수 내부에서 선언이 되었지만, 외부에서도 접근이 가능함
	var u = User{name,age}
    return &u
}

func main(){
	userPointer := NewUse("AAA",23)
    fmt.Println(userPointer)
}
  • 메모리 할당 시 스택 또는 힙에 저장됨
  • 이론상 스택이 더 효율적이지만, 스택 메모리는 함수 내부에서만 사용하고 힙은 함수 외부로 공개 됨
  • Go는 escape analysis를 통해 스택 또는 힙에 저장할 지를 결정함
  • 함수가 외부로 공개될 경우 함수가 종료되어도 사라지지 않음
profile
2929

0개의 댓글