안녕하세요, 주니어 개발자 Eon입니다.
이번 포스트에서는 구조체에 대해 다루겠습니다.
Structure 라고 한다.
위의 그림을 보면, 직원 한 명을 특정하기 위해 총 8개의 속성이 사용됐습니다.
구조체를 어떻게 사용하는지 아직은 모르지만, 저 속성들 모두 사용해야 한다면 아래와 같이 사용할 수 있습니다.
var Employee1_Name string = "Eon"
var Employee1_Rank string = "staff"
var Employee1_No uint32 = 200803
var Employee1_Department string = "Development"
var Employee1_Vacation int8 = 15
var Employee1_AnnualIncome uint64 = 50000000
var Employee1_Email string = "vamos_eon@email.com"
var Employee1_InCharge string = "backend"
정말 복잡합니다. 최대한 가독성 좋게 만들어 보려고 해도 표현할 직원이 몇이나 더 생길지도 모르고, 사용할 때마다 사이드 이펙트를 고려해야 합니다.
가령, Employee1
의 이름을 다른 사람으로 바꾼다면 그건 전혀 의미가 없는 정보가 됩니다.
따라서 무엇 하나 바꾼다면 그게 다른 속성에 영향을 끼치는지 확인 후에 전부 함께 바꿔야 합니다.
앞에 Employee1_
를 항상 써가면서 말입니다.
구조체를 사용하면 위의 그림처럼, 직원마다 다른 정보를 넣어 관리하기 편리해집니다.
구조체로 표현하면 값을 바꿀 때도, 새로 값을 넣을 때도 편하게 넣을 수 있습니다.
type 타입명 struct { 필드명 타입 ... 필드명 타입 }
작성할 구조체의 이름을 정합니다.
구조체의 이름은 변수를 선언할 때 작성하는 타입과 비슷하게 작성할 수 있습니다.
위의 사용법을 바탕으로 직원에 대한 구조체를 작성해보겠습니다.
type Employee struct {
Name string
Rank string
No uint32
Department string
Vacation int8
AnnualIncome uint64
Email string
InCharge string
}
구조체를 작성했습니다.
var 구조체변수명 구조체타입명
이어서, 구조체 변수를 선언하겠습니다.
var employee Employee
이제 구조체 변수 employee
은 구조체 Employee
가 가지는 필드를 모두 사용할 수 있습니다.
위에서 선언한 구조체 변수 employee
에 값을 초기화해서 사용해보겠습니다.
구조체 변수에 값을 대입하기 위해서는 아래의 방법들이 있습니다.
📍 방법 1. 각 필드를 따로 초기화
var employee Employee employee.Name = "Eon" employee.Rank = "staff" employee.No = 200803 employee.Department = "Development" employee.Vacation = 15 employee.AnnualIncome = 50000000 employee.Email = "vamos_eon@email.com" employee.InCharge = "backend" fmt.Println(employee) // {Eon staff 200803 Development 15 50000000 vamos_eon@email.com backend}
구조체 변수
employee
에 대하여 속성을 모두 초기화하고 출력했습니다.
주의 : 초기화하지 않으면 해당 필드에 대해서는 zero-value로 초기화됩니다.
📍 방법2. 모든 필드를 한 번에 초기화
var employee Employee employee = Employee{ "Kim", "Manager", 210109, "DEV", 20, 70000000, "kim@email.com", "back-end", } fmt.Println(employee) // {Kim Manager 210109 DEV 20 70000000 kim@email.com back-end}
필드 순서에 맞추어 값을 초기화할 수도 있습니다.
주의 : 중간에 초기화하지 않는 필드를 작성조차 하지 않으면 타입이 안 맞을 수 있어, 에러가 발생하기 때문에 zero-value로라도 꼭 초기화를 해야 합니다.
📍 방법3. 선택한 필드만 한 번에 초기화
var employee Employee = Employee{Name: "Eon", Email: "vamos_eon@email.com"} fmt.Println(employee) employee = Employee{Name: "Kim"} fmt.Println(employee) // {Eon 0 0 0 vamos_eon@email.com } // {Kim 0 0 0 }
선택한 필드만 초기화할 수도 있습니다.
주의 : 이미 초기화된 필드가 있는 상태에서 위와 같은 방법으로 대입을 하면, 선택된 필드 외에는 zero-value가 대입됩니다.
구조체를 포함하는 구조체는 조금 더 상세한 표현을 가능하게 합니다.
처음에 그린 구조와 조금 달라졌습니다.
처음에는 직원 한 명에 대해서만 정보를 넣으면 됐으니 그 한 명을 특정하고 정보를 입력했다면, 이번에는 부서 전체에 대하여 그 구성원들의 정보를 넣을 겁니다.
저렇게 많은 것들을 넣게 되면 개념을 잡을 때 복잡할 수 있으니, 일단은 아래 그림과 같이 부서를 중심으로 직원1에 대해서만 구조체를 간단히 선언해보겠습니다.
type 구조체명1 struct { 필드명 타입 ... 필드명 구조체명2 ... } type 구조체명2 struct { 필드명 타입 ... 필드명 타입 ... }
구조체 안에 타입을 구조체로 둠으로써, 구조체 안의 구조체를 선언했습니다.
위의 사용법을 바탕으로 부서에 대한 구조체를 작성해보겠습니다.
type Department struct {
DepName string
Location string
Project string
Employee1 Employee
}
type Employee struct {
Name string
Rank string
No uint32
Vacation int8
AnnualIncome uint64
Email string
InCharge string
}
구조체를 작성했습니다.
var 구조체변수명 구조체타입명
이어서, 구조체 변수를 선언하겠습니다.
var department Department
이제 구조체 변수 department
는 구조체 Department
가 가지는 필드를 모두 사용할 수 있습니다.
위에서 선언한 구조체 변수 department
에 값을 초기화해서 사용해보겠습니다.
구조체 변수를 초기화하기 위한 방법은 위에서 소개한 구조체 변수의 초기화와 동일합니다.
다만, 구조체 내부의 구조체에 접근하려면 점(.) 을 두 번 사용해서 접근합니다.
📍 방법 1. 각 필드를 따로 초기화
var department Department department.DepName = "Development" department.Location = "8F" department.Project = "Hello World!!" department.Employee1.Name = "Eon" department.Employee1.Rank = "staff" department.Employee1.No = 200803 department.Employee1.Vacation = 15 department.Employee1.AnnualIncome = 50000000 department.Employee1.Email = "vamos_eon@email.com" department.Employee1.InCharge = "backend" fmt.Println(department) // {Development 8F Hello World!! {Eon staff 200803 15 50000000 vamos_eon@email.com backend}}
구조체 변수
department
대하여 속성을 모두 초기화하고 출력했습니다.주의 : 초기화하지 않으면 해당 필드에 대해서는 zero-value로 초기화됩니다.
📍 방법2. 모든 필드를 한 번에 초기화
var department Department = Department{ "Development", "8F", "Hello World!!", Employee{ "Eon", "staff", 200803, 15, 50000000, "vamos_eon@email.com", "backend", }, } fmt.Println(department) // {Development 8F Hello World!! {Eon staff 200803 15 50000000 vamos_eon@email.com backend}}
필드 순서에 맞추어 값을 초기화할 수도 있습니다.
구조체 안의 구조체답게 필드 값을 초기화할 때 구조체 형태로 초기화합니다.주의 : 중간에 초기화하지 않는 필드를 작성조차 하지 않으면 타입이 안 맞을 수 있어, 에러가 발생하기 때문에 zero-value로라도 꼭 초기화를 해야 합니다.
📍 방법3. 선택한 필드만 한 번에 초기화
var department Department = Department{ DepName: "Development", Employee1: Employee{ Name: "Eon", Email: "vamos_eon@email.com", }, } fmt.Println(department) // {Development {Eon 0 0 0 vamos_eon@email.com }}
선택한 필드만 초기화할 수도 있습니다.
var department Department = Department{ "Development", "8F", "Hello World!!", Employee{ Name: "Eon", Email: "vamos_eon@email.com", }, } fmt.Println(department) // {Development 8F Hello World!! {Eon 0 0 0 vamos_eon@email.com }}
부분적으로 필드 선택 방식을 사용할 수도 있습니다.
주의 : 이미 초기화된 필드가 있는 상태에서 위와 같은 방법으로 대입을 하면, 선택된 필드 외에는 zero-value가 대입됩니다.
말이 굉장히 길고 복잡합니다. 하지만 다르게 표현할 수 있는 방법을 모르겠습니다.
굳이 표현한다면 '내장 구조체' 정도로 정리할 수 있겠습니다.
embedded struct field 방식이라고 합니다.
embedded struct field 방식은 구조체 필드를 선언할 때, 필드명을 작성하지 않고 타입만 선언합니다.
type 구조체명1 struct { 필드명 타입 ... 구조체명2 ... } type 구조체명2 struct { 필드명 타입 ... }
필드명 없이, 구조체 타입인 구조체명만 적어서 내장 구조체를 선언합니다.
위의 선언 방법을 바탕으로 구조체를 작성해보겠습니다.
type Department struct {
DepName string
Location string
Project string
Employee
}
type Employee struct {
Name string
Rank string
No uint32
Vacation int8
AnnualIncome uint64
Email string
InCharge string
}
내장 구조체 선언을 했습니다.
위 Department
구조체 내의 Employee
구조체가 내장 구조체로 선언된 것을 볼 수 있습니다.
위에서 선언한 내장 구조체를 포함하는 구조체 변수 department
에 값을 초기화해서 사용해보겠습니다.
내장 구조체 변수를 초기화하기 위한 방법은 지금까지와는 다릅니다.
초기화할 때 필드명을 적어주었던 게 전부 빠지고 구조체명으로 대체되기 때문입니다.
특이한 점으로는 내장 구조체 필드에 접근할 때, 점(.) 을 하나만 사용해도 됩니다.
📍 방법 1. 각 필드를 따로 초기화
var department Department department.DepName = "Development" department.Location = "8F" department.Project = "Hello World!!" department.Name = "Eon" department.Rank = "staff" department.No = 200803 department.Vacation = 15 department.AnnualIncome = 50000000 department.Email = "vamos_eon@email.com" department.InCharge = "backend" fmt.Println(department) // {Development 8F Hello World!! {Eon staff 200803 15 50000000 vamos_eon@email.com backend}}
구조체 변수
department
에 대하여 속성을 모두 초기화하고 출력했습니다.
필드명이 없기 때문에 내장 구조체의 필드에 접근할 때는 구조체에서 바로 접근할 수 있습니다.주의 : 초기화하지 않으면 해당 필드에 대해서는 zero-value로 초기화됩니다.
📍 방법2. 모든 필드를 한 번에 초기화
var department Department = Department{ "Development", "8F", "Hello World!!", Employee{ "Eon", "staff", 200803, 15, 50000000, "vamos_eon@email.com", "backend", }, } fmt.Println(department) // {Development 8F Hello World!! {Eon staff 200803 15 50000000 vamos_eon@email.com backend}}
이 방법은 위에 소개한 구조체를 포함하는 구조체 변수의 초기화 방법2 와 동일합니다.
주의 : 중간에 초기화하지 않는 필드를 작성조차 하지 않으면 타입이 안 맞을 수 있어, 에러가 발생하기 때문에 zero-value로라도 꼭 초기화를 해야 합니다.
📍 방법3. 선택한 필드만 한 번에 초기화
var department Department = Department{ DepName: "Development", Employee: Employee{ Name: "Eon", Email: "vamos_eon@email.com", }, } fmt.Println(department) // {Development {Eon 0 0 0 vamos_eon@email.com }}
선택한 필드만 초기화할 수도 있습니다.
단, 보시는 바와 같이 구조체명을, 필드명 대신 작성해야 합니다.
var department Department = Department{ "Development", "8F", "Hello World!!", Employee{ Name: "Eon", Email: "vamos_eon@email.com", }, } fmt.Println(department) // {Development 8F Hello World!! {Eon 0 0 0 vamos_eon@email.com }}
부분적으로 필드 선택 방식을 사용할 수도 있습니다.
주의 : 이미 초기화된 필드가 있는 상태에서 위와 같은 방법으로 대입을 하면, 선택된 필드 외에는 zero-value가 대입됩니다.
type 구조체명1 struct { 필드명1 타입 ... 구조체명2 ... } type 구조체명2 struct { 필드명1 타입 ... }
위와 같이 서로 다른 구조체에 필드명이 중복되어 있고, 그 두 구조체가 내장 구조체의 관계를 가지고 있습니다.
이 때, 각 구조체의 필드명1에 값을 서로 다르게 부여하고 두 필드명1에 모두 접근할 수 있습니다.
위의 선언 방법을 바탕으로 구조체를 작성해보겠습니다.
type Department struct {
Name string
Location string
Project string
Employee
}
type Employee struct {
Name string
Rank string
No uint32
Vacation int8
AnnualIncome uint64
Email string
InCharge string
}
두 구조체의 Name
이라는 필드명이 중복되어 있는 것을 볼 수 있습니다.
같은 필드명을 가지고 있는 내장 구조체 변수의 초기화 방법은 또 조금 다릅니다.
초기화할 때 필드명을 적어주었던 게 전부 빠지고 구조체명으로 대체되기 때문입니다.
특이한 점으로는 내장 구조체 필드에 접근할 때, 점(.) 을 하나만 사용해도 됩니다.
📍 방법 1. 각 필드를 따로 초기화
var department Department department.Name = "Development" department.Location = "8F" department.Project = "Hello World!!" department.Employee.Name = "Eon" department.Rank = "staff" department.No = 200803 department.Vacation = 15 department.AnnualIncome = 50000000 department.Email = "vamos_eon@email.com" department.InCharge = "backend" fmt.Println(department) // {Development 8F Hello World!! {Eon staff 200803 15 50000000 vamos_eon@email.com backend}}
구조체 변수
department
에 대하여 속성을 모두 초기화하고 출력했습니다.
필드명이 없기 때문에 내장 구조체의 필드에 접근할 때는 구조체에서 바로 접근할 수 있습니다.같은 필드명의 내장 구조체 내의 필드에 접근하려면 구조체명을 사용해서 접근할 수 있습니다.
department.Employee.Name = "Eon"
같은 필드명이 아니더라도 구조체명을 사용해서 접근할 수도 있습니다.
주의 : 초기화하지 않으면 해당 필드에 대해서는 zero-value로 초기화됩니다.
📍 방법 2. 모든 필드를 한 번에 초기화
위에서 소개한 방식과 같습니다.
📍 방법3. 선택한 필드만 한 번에 초기화
위에서 소개한 방식과 같습니다.
구조체도 배열의 형태로 저장할 수 있습니다.
위에서 보여드린 구조체에 대한 그림 중, 아래의 그림을 예시로 사용하겠습니다.
type 구조체명1 struct { 필드명 타입 ... 필드명 [배열크기]구조체명2 ... } type 구조체명2 struct { 필드명 타입 ... }
위와 같은 방법으로 구조체 배열을 포함하는 구조체를 선언합니다.
위의 선언 방법을 바탕으로 구조체를 작성해보겠습니다.
type Department struct {
DepName string
Location string
Project string
EmployeeInfo [1]Employee
}
type Employee struct {
Name string
Rank string
No uint32
LeaveInfo Leave
AnnualIncome uint64
Email string
InCharge [1]Tasks
}
type Leave struct {
AnnualLeave uint8
BereavementLeave uint8
LongServiceLeave uint8
SickLeave uint8
}
type Tasks struct {
Name string
Duration uint8
Progress uint8
}
var OurComapny [1]Department = [1]Department{} OurComapny[0].DepName = "Development" OurComapny[0].Location = "8F" OurComapny[0].Project = "Hello World!!" OurComapny[0].EmployeeInfo[0].Name = "Eon" OurComapny[0].EmployeeInfo[0].Rank = "staff" OurComapny[0].EmployeeInfo[0].Email = "vamos_eon@email.com" OurComapny[0].EmployeeInfo[0].AnnualIncome = 50000000 OurComapny[0].EmployeeInfo[0].No = 200803 OurComapny[0].EmployeeInfo[0].LeaveInfo.AnnualLeave = 15 OurComapny[0].EmployeeInfo[0].LeaveInfo.BereavementLeave = 5 OurComapny[0].EmployeeInfo[0].LeaveInfo.LongServiceLeave = 0 OurComapny[0].EmployeeInfo[0].LeaveInfo.SickLeave = 30 OurComapny[0].EmployeeInfo[0].InCharge[0].Name = "Golang installation" OurComapny[0].EmployeeInfo[0].InCharge[0].Duration = "~2022-01-16" OurComapny[0].EmployeeInfo[0].InCharge[0].Progress = "80%"
위와 같이 초기화해서 접근할 수 있습니다.
slice
로 선언하면 훨씬 유동적이고 쉽게 초기화 및 접근이 가능합니다.
slice
에 대한 내용은 나중에 다루겠습니다.
구조체의 메모리 할당에 가장 큰 특징은 메모리 패딩입니다.
빈 공간을 추가로 할당하는 것인데, 왜 그런지 알아보겠습니다.
운영체제에 대한 이야기를 조금이라도 들어봤다면 32비트 운영체제와 64비트 운영체제가 있다는 것은 알 수 있습니다.
요즘 나오는 운영체제는 대부분이 64비트 운영체제입니다.
더 많은 양을 처리할 수 있기 때문에 뭔가 특별한 제약이 있는 경우가 아니라면 32비트를 쓸 이유가 없습니다.
이 32비트와 64비트가 의미하는 것은 바로 '한 번에 처리할 수 있는 데이터의 크기' 라고 보시면 됩니다.
정확히는 주소의 길이입니다.
32비트 운영체제는 주소의 단위가 4바이트입니다. 따라서 한 번에 처리할 수 있는 데이터의 크기는 4바이트입니다.
64비트 운영체제는 주소의 단위가 8바이트입니다. 따라서 한 번에 처리할 수 있는 데이터의 크기는 8바이트입니다.
우리가 코딩을 하고 컴파일한 후에 실행하면, 선언한 변수들에 대한 메모리가 할당이 됩니다.
그 변수가 사용될 때, 데이터가 RAM에 먼저 올라가게 되고, CPU가 연산을 하기에 앞서, Register에 해당 데이터를 가지고 온 다음에 CPU가 Register에 올라온 데이터를 가지고 연산을 합니다.
32비트 운영체제에서 8바이트짜리 데이터는 두 번 읽어서 사용합니다.
DRAM과 캐시를 이용하는데, 이것은 운영체제에 관련한 공부를 따로 요구합니다.
아무튼 32비트 운영체제에서도 8바이트짜리 데이터를 읽고 사용하는 데에는 문제가 없습니다.
대개 64비트 운영체제를 사용하기 때문에, 운영체제는 메모리 할당을 8바이트의 배수 단위로 하려고 할 것입니다.
이것이 '메모리 정렬' 입니다.
64비트 운영체제는 메모리를 8바이트 단위로 할당한다고 했습니다.
한 번에 처리할 수 있는 데이터의 최대 크기가 8바이트이기 때문입니다.
bool
변수를 하나 선언하면 1바이트입니다.
int16
변수를 하나 선언하면 2바이트입니다.
이 두 변수를 선언하면 총 3바이트입니다.
그런데, 위의 두 변수를 하나의 구조체에서 사용하면 메모리 정렬이 일어납니다.
변수를 따로 선언하는 것은, 메모리의 어디에든 따로 할당되어 있어도 상관없습니다.
하지만 구조체는 구조체 자체가 하나의 변수처럼 되어 있는 구조이므로 연속된 메모리에 할당됩니다.
구조체 안에 bool
과 int16
을 선언하겠습니다.
type structure1 struct {
vBool bool
vInt16 int16
}
...
fmt.Println(unsafe.Sizeof(structure1{})
// 4
structure1
의 크기는 4바이트입니다.
메모리 정렬로 인해 생긴 메모리 패딩 때문입니다.
메모리 정렬은 CPU가 메모리에서 데이터를 가지고 올 때 효율적으로 동작하기 위해 수행합니다.
메모리 정렬은 구조체에 선언된 변수의 순서대로 진행되며, 구조체 내의 변수 중 가장 큰 사이즈가 기준이 됩니다.
위의 경우, vInt16
이 structure1
내의 가장 사이즈가 큰 변수이므로 int16
의 사이즈인 2바이트가 데이터를 불러올 때의 단위가 됩니다.
그래서 bool
타입의 vBool
을 메모리에 할당할 때, 2바이트로 할당하게 되는 것입니다.
그렇게 bool
타입의 실제 사이즈인 1바이트가 아닌 해당 변수에 2바이트가 할당되어 사용하지 않는 1바이트가 생겨나게 됩니다.
이것이 메모리 패딩입니다.
예시를 한 가지 더 보겠습니다.
아래와 같이 구조체를 선언해보겠습니다.
type structure2 struct {
v1Int32 int32
vInt16 int16
v2Int32 int32
vBool bool
}
보시는 바와 같이 구조체 내의 가장 큰 변수 사이즈인 int32
에 맞추어, 4바이트를 기준으로 메모리 할당이 된 것을 볼 수 있습니다.
4바이트 간격으로 int16
은 2바이트를 차지하고, 전체 4바이트 공간 중 나머지 2바이트는 사용하지 않는 메모리 패딩으로 남습니다.
구조체를 만들 때 필드의 사이즈를 내림차순으로 만드는 것이 가장 좋습니다.
자료형들의 사이즈를 보면 모두 2의 거듭제곱으로 표현된다는 것을 알 수 있습니다.
각각 1, 2, 4, 8 바이트입니다.
내림차순으로 만들게 되면 메모리 패딩을 줄이고 구조체의 크기를 줄일 수 있습니다.
type structure2 struct {
v1Int32 int32
v2Int32 int32
vInt16 int16
vBool bool
}
위에서 잔뜩 메모리 정렬과 메모리 패딩에 대해서 이야기했는데 정작 메모리가 실제로 어떻게 잡히는지 사이즈 측정을 하지 않았습니다.
여기서는 사이즈를 구해보도록 하겠습니다.
unsafe 패키지 를 이용하면 됩니다.
unsafe 패키지는 안전하지 않은 유형들을 포함하는 패키지입니다.
사용할 때는 조심해야 하며 Go 1 호환성 가이드라인으로 보호받지 않습니다.
ArbitraryType
은 실제 unsafe
패키지에 포함된 타입이 아닙니다.
문서화를 위한 타입일 뿐입니다.
Sizeof()
함수는 그 타입의 크기를 바이트 단위로 반환합니다.
단, 참조되는 메모리의 크기를 그대로 반환하는 것은 아닙니다.
slice
와 같은 경우, 메모리의 크기가 아니라 slice
설명자의 크기를 반환한다고 되어 있습니다.
여기서, 변수의 크기와 구조체의 크기를 구해보겠습니다.
package main import ( "fmt" "unsafe" ) func main() { fmt.Printf("Size of %T : %d\n", int64(0), unsafe.Sizeof(int64(0))) fmt.Printf("Size of %T : %d\n", int32(0), unsafe.Sizeof(int32(0))) fmt.Printf("Size of %T : %d\n", int16(0), unsafe.Sizeof(int16(0))) fmt.Printf("Size of %T : %d\n", int8(0), unsafe.Sizeof(int8(0))) fmt.Printf("Size of %T : %d\n", bool(false), unsafe.Sizeof(bool(false))) } /* The result of the ouput Size of int64 : 8 Size of int32 : 4 Size of int16 : 2 Size of int8 : 1 Size of bool : 1 */
golang의 자료형 사이즈대로 잘 나오는 것을 확인할 수 있습니다.
구조체의 크기도 구해보겠습니다.
package main import ( "fmt" "unsafe" ) type structure1 struct { vBool bool vInt16 int16 } func main() { var s structure1 fmt.Printf("Size of %T struct: %d bytes\n", s, unsafe.Sizeof(s)) } // Size of main.structure1 struct: 4 bytes
위에서 설명드린 바와 같이,
bool
타입에 1바이트의 메모리 패딩이 붙어, 총 4바이트가 된 것을 확인할 수 있습니다.
string
의 크기를 구해보겠습니다.
string
을 Sizeof()
함수로 크기를 구해보면 string
변수의 값이 말도 안 되게 길어도 항상 16바이트를 반환합니다.
fmt.Printf("Size of %T : %d\n", string("abcdefghijklmnopqrstuvwxyz"), unsafe.Sizeof(string("abcdefghijklmnopqrstuvwxyz")))
// Size of string : 16
unsafe.Sizeof(string(""))
은 string
의 설명자의 크기를 반환하기 때문입니다.
string
은 StringHeader
, 바이트 데이터로 구성되어 있습니다.
type StringHeader struct {
Data uintptr
Len int
}
StringHeader
는 위와 같습니다.
Data
는 실제 데이터가 있는 위치, Len
은 문자열 길이를 저장합니다.
따라서, StringHeader
의 크기는 uintptr(8bytes) + int(8bytes)
로, 16바이트가 됩니다.
string
타입의 데이터가 실제로 메모리에 할당되는 크기는 아래와 같습니다.
var str string = "Hello" stringSize := len(str) + int(unsafe.Sizeof(str)) fmt.Println(stringSize) // 21
string 타입의 실제 메모리 = 문자열 길이 + StringHeader의 크기
unsafe.Sizeof(string(""))
은 StringHeader
의 크기를 반환한다고 했습니다.
구조체에서는 필드 중 가장 사이즈가 큰 값을 기준으로 메모리를 할당한다고 했습니다.
string
이 끼어 있는 경우, 메모리 할당이 어떻게 이루어지는지 보겠습니다.
type strInclude struct {
vStr string
vInt64 int64
}
...
fmt.Println(unsafe.Sizeof(strInclude{}))
// 24
이렇게 할당됩니다.
이유는 간단합니다.
unsafe.Sizeof(strInclude{})
는 StringHeader
의 사이즈를 반환하고, StringHeader
는 uintptr, int
로 이루어져 있으며 이 두 자료형 모두 8바이트입니다.
따라서 아래와 같이 8바이트 단위로 메모리 정렬 및 할당이 이루어집니다.
당연하게도, strInclude
구조체 내에 int64
자료형의 필드가 int32
가 되더라도 strInclude
의 크기는 24로 나오게 됩니다.
이번 포스팅은 구조체에 대한 내용이었습니다.
감사합니다.👍
좋은 자료 감사합니다!