구조체

여러 필드를 묶어서 사용하는 타입으로 C의 structure와 비슷하며, go에서는 별도의 클래스를 키워드를 제공하지 않지만 구조체를 이용해서 클래스를 정의할 수 있다.


1. 선언

/*
type 이름 sturct{
  ... 필드명
}
*/
type School struct{
  Name string
  CntTeacher int
}

type Student struct {
  school School
  Name string
  score float64
}

//Student형 변수 선언
var student Student
student.Name = "홍길동"
student.score = 87.1

struct을 이용해서 구조체를 선언하고 해당 형식의 구조체를 특정 이름이라는 타입으로 부르도록 하겠다라는 의미로 type을 이용해서 선언을 해주면 된다.


//student라는 패키지에 선언
type Student struct {
  schoolInfo School
  Name string
  score float64
}

//main 패키지
import (
	"goStudy/student"
)

func main() {
	student := student.Student{}
  student.score = 98.1  //error
}

구조체내부의 필드로 다른 구조체 타입을 포함할 수 도 있다. 이때 go만의 특징으로 go는 특정 문법을 강제하는 강타입 언어인데 구조체 내부의 필드를 패키지 외부에서 접근하기 위해서는 이름은 반드시 대문자로 시작을 해야 한다.
소문자로 시작을 한다면 같은 패키지 내부에서만 접근 할 수 있다.



2. 초기화

var student Student  //zero value를 갖는 구조체 변수

var stdeunt Student = Student{"홍길동", 12.1}
student := Student{"홍길동", 12.1} //타입 추론



3. 포함된 필드방식(Embedded field)

type School struct{
  Name string
  CntTeacher int
}

type Student struct {
  School        //Embedded field
  Name string
  score float64
}

구조체 내부에 변수명 없이 타입만 설정한 field로 다른 기본형 타입은 사용이 불가능하고 구조체타입만 사용이 가능하다. 해당 필드방식으로 사용하면 School 구조체타입의 내부 필드가 Student 필드로 들어가게 되어 변수명으로 접근을 하지 않고 바로 접근을 할 수 있다.

var stdeunt Student

student.schoolInfo.Name = "길동고등학교" //기존의 방식
student.CntTeacher = 4  //Embedded field사용한 경우
student.School.Name = "길동고등학교" //필드명이 중복이 될 경우에는 구조체 타입을 이용해서 구분해주어야 한다.



4. 구조체 크기

type User struct{
  Age int
  Score float64
}

var user User
alter-text

각 필드 타입의 크기를 모두 더한만큼 변수의 크기가 설정 된기 때문에 user는 8Byte(int) + 8Byte(float64) = 16byte가 된다.



5. 구조체 복사

type User struct{
  Age int
  Score float64
}

var user1 User = User{23, 87.1}

var user2 User = user1

대입연산자를 이용해 새로운 변수에 값을 할당 해주면 go는 메모리의 값을 그대로 새로운 메모리에 할당을 해준다. 이는 Java의 class 변수를 =연산자로 할당해주면 reference를 넘겨주는 것과는 다르게 동작하기 때문에 헷갈리지 말자. (자바는 얕은 복사, go는 깊은 복사)


user2.Age = 56

fmt.Println(user1) // {23 87.1}
fmt.Println(unsafe.Sizeof(user1)) //16

fmt.Println(user2) // {56 87.1}
fmt.Println(unsafe.Sizeof(user2)) //16

user3 := &user
user3.Age = 1
fmt.Println(user1) // {1 87.1}

Reference를 넘겨주고 싶으면 포인터 타입(*)과 메모리주소를 넘겨주기 위한 키워드 &를 사용하면 된다. (C와 비슷하다.)


추가)

go의 표준 패키지로 unsafe라는 패키지가 있는데 이는 이름 그대로 안전하지 않은 함수를 제공한다. 그 중에 SizeOf() 는 변수가 차지하고 있는 메모리 크기(Byte)를 반환하는 함수 이다.

type User struct{
  Age int32
  Score float64
}

var user1 User
fmt.Println(unsafe.Sizeof(user1)) //16

위에서 설명한대로라면 int32는 4byte, float64는 8byte이기 때문에 user는 12byte를 가져야 하는데 실제로는 16byte의 크기를 갖는다. 이는 컴퓨터(go)가 컴퓨터의 사양(32bit컴퓨터/64bit 컴퓨터)에 따라 관리하기 편한 방법으로 메모리를 정렬하기 때문이다.

현재에는 대부분 64bit컴퓨터로 8byte단위로 값을 가져오는데 합이 12byte로 4byte가 끊기기 때문에 조금 더 효율적인 관리를 위해 4byte만큼을 추가 메모리를 할당(Memory padding)해서 관리를 한다. 이는 Age가 8Byte를 갖는 것이 아니라 4byte의 빈공간을 만들어 관리하는 것이다.

한마디로 성능을 위해서 메모리를 희생하게 된다.


type User struct{
  a int8
  b float64
  c int8
}

var user1 User
fmt.Println(unsafe.Sizeof(user1)) //24

이는 위에서 설명한대로 8byte씩 끊어서 관리하기 때문에 24byte만큼 할당이 되었는데 8byte보다 작은 필드라면 앞으로 몰아서 필드를 선언하는 것이 메모리 패딩을 줄여 메모리를 절약할 수 있다.

type User struct{
  a int8
  c int8
  b float64
}

var user1 User
fmt.Println(unsafe.Sizeof(user1)) //16



6. 구조체의 역할

결합도는 낮게 응집도는 높게

함수는 비슷한 코드를, 배열은 같은 타입의 데이터를 묶어 응집도를 높이는 것이라면 구조체는 관련된 데이터를 묶어 응집도를 높여 재사용성을 증가시키는데 사용한다.

go는 처음에 설명한 것처럼 class가 존재하지 않고 structure가 객체로써 사용되기 위한 방법으로 사용된다.





Reference

『Tucker의 Go 언어 프로그래밍』 스터디 요약 노트

Tucker의 Go 강좌

Tags :

Related Posts

Permutation(순열)

Permutation(순열)

1. next_permutation c++에는 algorithm헤더에 매개변수의 배열/iterator의 다음 순열을 적용시켜 바뀌었다면 true/false를 반환해주는 메서드가 존재해서 이를 do~while문으로 쉽게 순열문제를 해결할 수 있다. 하지만, 자바는 존재하지 않기 때문에 다음과 같이 구현할 수 있다. public boolean next_permutation(int[] arr){ //뒤에서부터 탐색해서 내림차순이 깨...

Read More
연산자

연산자

  • Java
  • 2021년 1월 23일

백기선님의 유튜브 로 진행하시는 스터디를 진행하며 올리는 정리 블로그입니다. 산술 연산자 두개의 피연산자를 갖는 이항 연산자로써, 기본적인 사칙연산을 다루는 연산자 ◾ 더하기 (+) 왼쪽의 피연산자에 오른쪽 피연산자를 더하는 연산자로 숫자+숫자, 문자열+문자열이 가능하고 문자열+숫자를 할 시 숫자를 자동으로 문자열로 변환하여 덧셈이 가능하다....

Read More
클래스

클래스

  • Java
  • 2021년 2월 1일

객체 지향의 중심 객체를 정의하는 일종의 틀 같은 것으로 상태(변수) / 행동(메서드)이 있을 수 있다. 이로 인해 추상화, 캡슐화, 상속, 다형성 등을 가능하게 하여 개발 / 유지보수를 쉽게 해준다. 1. 클래스 정의하는 방법 class키워드를 통해 클래스를 새로 정의할 수 있다. class Fruit{ private String name; Fruit(String name){this.name = name;} public String getName(){ return name; } } 1) 필드 변수를 뜻하며 두가지 종류가 존...

Read More