Post

11. 구조체

11. 구조체

✅ 1. 구조체 선언

1
2
3
4
5
6
type Student struct {
	Name string
	Class int
	No int
	Score float64
}
  • type 키워드로 사용자 정의 타입임을 정의한다
  • 타입명의 첫 번째 글자가 대문자면 패키지 외부로 공개되는 타입이다
  • 타입 종류인 struct를 적는다

✅ 2. 구조체 변수 초기화

2.1 초깃값 생략

  • 초깃값을 생략하면 모든 필드가 기본값으로 초기화된다
    1
    
      var house House
    

    2.2 모든 필드 초기화

  • 모든 필드값을 중괄호 사이에 넣어 초기화한다
  • 모든 필드가 순서대로 초기화된다

    1
    2
    3
    4
    5
    6
    7
    8
    
    var house House = House{"관악구", 27, 9.0, "아파트"}
      
    var house House = House{
    	"관악구", 
    	27, 
    	9.0, 
    	"아파트", // 여러 줄 초기화 시 마지막 값 뒤에 쉼표를 달아야 한다
    }
    

2.3 일부 필드 초기화

  • 일부 필드만 초기화하는 경우 필드명: 필드값 형식으로 초기화한다. 나머지 변수는 기본값으로 초기화된다

    1
    2
    3
    4
    5
    6
    
    var b Student = Student{no: 1, Class: 2}
      
    var c Student = Student{
    	no: 1,
    	Class:2,
    }
    

✅ 3. 구조체를 포함하는 구조체

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type VIPUser struct {
	UserInfo User
	Level int
}

func main() {
vip := VIPUser {
	User{1, "Nam", 2},
	3,
}

fmt.Printf("Id: %d\nName: %s\n",
vip.UserInfo.Id, vip.Name}
// vip.Name undefined (type VIPUser has no field or method)
  • 구조체 내에 구조체 변수를 포함하는 경우, 내부 구조체 변수에 접근하려면 vip.UserInfo.Name 형식으로 접근해야 한다

3.1 포함된 필드 방식

  • 위 방식처럼 접근하지 않고 구조체 내 구조체의 필드에 직접 접근하고 싶다면 구조체 필드명을 생략해야한다
  • 단, 필드명이 중복일 경우 외부 구조체 필드에 먼저 접근하므로, 내부 구조체 필드에 접근하기 위해서는 vip.User.Level과 같이 구조체명을 쓴 후 접근해야한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type VIPUser struct {
	User
	Level int
}

func main() {
vip := VIPUser {
	User{1, "Nam", 2},
	3,
}

fmt.Printf("Id: %d\nName: %s\nVIP Level: %v\nLevel: %v\n",
	vip.Id, vip.Name, vip.User.Level, vip.Level)
	
// 내부 구조체의 필드는 vip.User.Level로 접근 가능

✅ 4. 구조체 값 복사

  • 구조체 변수를 다른 구조체에 대입하면 모든 필드값이 복사된다
  • Go 내부에서는 필드 각각이 아닌 구조체 전체를 복사한다

✅ 5. 구조체 크기

5.1 메모리 정렬

  • 컴퓨터가 데이터에 효과적으로 접근하고자 메모리를 일정 크기 간격으로 정렬하는 것
  • 레지스터 크기가 8바이트인 64비트 컴퓨터는 한 번에 8바이트 크기를 연산할 수 있다.
  • 따라서 데이터가 레지스터 크기와 동일한 크기로 정렬되어 있으면 더욱 효율적으로 데이터를 읽어올 수 있다.
  • 4바이트 필드, 8바이트 필드가 있는 구조체 변수가 메모리에 저장되는 경우, 4바이트 필드와 8바이트 필드 사이에 4바이트의 공간이 생기는데, 이를 메모리 패딩이라고 한다

5.2 메모리 패딩을 고려한 필드 배치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type User1 struct {
	A int8
	B int64
	C int8
	D int64
	E int8
}

type User2 struct {
	A int8
	C int8
	E int8
	B int64
	D int64
}

func main() {
	user1 := new(User1)
	user2 := new(User2)

	fmt.Println(unsafe.Sizeof(*user1)) // 40
	fmt.Println(unsafe.Sizeof(*user2)) // 24
}
  • User1
    • 1바이트 필드 A를 저장한 후 8바이트 필드 B를 저장할 때 7바이트 x 3만큼의 메모리 패딩이 발생한다
    • OXXXXXXX | OOOOOOOO | OXXXXXXX | OOOOOOOO | OXXXXXXX → 40바이트
  • User2
    • 1바이트 필드 A, C, E를 몰아서 저장하여 5바이트만큼의 메모리 패딩이 발생한다
    • OOOXXXXX | OOOOOOOO | OOOOOOOO → 24바이트
This post is licensed under CC BY 4.0 by the author.