본문 바로가기

구조체 struct 본문

💘 C++/변수범위, 변수형

구조체 struct

Hyonii 2022. 1. 23. 19:49

구조체 struct

객체를 표현하기 위해 하나 이상의 변수가 필요한 프로그래밍 사례가 많다.

예를 들어 사람을 묘사한다고 생각해보면 

 

이렇게 다양한 요소들을 표현 할 수 있다.

위에는 어떻게든 그룹화 되지 않은 4개의 독립 변수가 있다.

한 사람에 대한 정보를 함수에 전달하려면 각 변수를 개별적으로 전달해야한다. 

또한, 다른 사람에 대한 정보를 저장하려면 추가된 사람마다 4개의 변수를 추가로 선언해야 한다.

 

다행히도 C++ 에서는 고유한 사용자 정의 집계 데이터 유형(user-defined aggregate data type)을 생성할 수 있다. 

집계 데이터 유형(aggregate data type)은 여러 개별 변수를 함께 그룹화하는 데이터 유형이다.

가장 단순한 집계 데이터 유형 중 하나는 구조체(struct)다.

 

즉, 구조체(struct)는 하나 이상의 변수를 그룹 지어서 새로운 자료형을 정의하는 것이다.

구조체 선언 및 정의 (Declaring and defining structs)

구조체(struct)는 사용자 정의 자료형이기 때문에,

먼저 그것을 사용하기 전에 구조체가 어떻게 생겼는지 컴파일러에게 말해야한다.

이를 위해 struct 키워드를 사용해 구조체를 선언해야 한다.

struct Person
{
    double height;
    float  weight;
    int    age;
    string name;
};

위 코드는 컴파일러에 Person 구조체를 정의한다고 말한다.

Person 구조체는 네 개의 변수를 포함한다.

구조체의 일부인 이러한 변수를 멤버(member) 또는 필드(field)라고 한다.

 

Person은 단지 선언에 불과하다.

컴파일러에 구조체가 멤버변수가 있을 것이라고 말하고 있지만, 지금은 어떤 메모리도 할당 되지 않는다.

일반적으로 구조체 이름은 대문자로 시작하여 변수이름과 구분한다.

 

경고: C++ 에서 가장 쉬운 실수 중 하나는 구조체 선언의 끝에 세미콜론을 잊어버리는 것이다.

 

Person 구조체를 사용하려면 Person 타입의 변수를 선언하면 된다.

 

구조체 Person은 대문자로 시작하고, 변수 me는 소문자로 시작한다.

위 코드는 me 라는 구조체 Person 타입의 변수를 정의한다.

일반 변수와 마찬가지로 구조체 변수를 정의하면 해당 변수에 대한 메모리가 할당된다.

구조체 멤버 접근(Accessing struct members)

Person me 와 같은 변수를 정의할때, 변수 me는 멤버 변수를 포함하는 전체 구조체(struct)를 참조한다.

개별 멤버에 접근하기 위해서는 멤버 선택 연산자(member selection operator: . )을 사용하면 된다.

 

다음은 구성원 멤버 선택 연산자를 사용하여 각 멤버 변수를 초기화 하는 예제이다.

여기서 초기화 하는 방법은

 

예전에는 이런식으로 했었는데 
요즘에는 { } uniform initializtion 을 이용하여 초기화 한다. 

 

구조체 멤버 변수는 일반 변수와 같게 작동한다.

 

사람에 대해서 프린트 하는 함수를 하나 만든다고 생각해보면

 

정상적으로 내가 입력한 대로 나온다.

C++11/14: Non-static member initialization

C++11부터는 비-정적(non-static)멤버 변수에 기본값을 지정 할 수 있다.

struct Rectangle
{
    double length = 1.0;
    double width = 1.0;
};

int main()
{
    Rectangle x; //length = 1.0, width = 1.0;
    
    x.length = 2.0; //you can assign other values like normal
    
    return 0;
}

그러나 불행하게도, 멤버 초기화 구문이 멤버 초기화 목록 밀 유니폼 초기화와 호환되지 않는다.

예를 들어 C++11에서는 다음 프로그램이 컴파일 되지 않는다.

struct Rectangle
{
    double length = 1.0;
    doubel width  = 1.0;
};

int main()
{
    Rectangle x{2.0, 2.0}; //uniform initialization
    
    return 0;
}

//Compile Error!

따라서 C++11에서는 비정적 멤버 초기화와 유니폼 초기화 중 무엇을 사용할지 정해야 한다.

그러나 C++14에서는 이 제한이 해제되어 두 가지 모두 사용가능.

둘 다 사용하면 초기화 목록/유니폼 초기화를 우선한다.

구조체와 함수 (Structs and functions)

개별 변수보다 구조체의 큰 장점은 구조체를 멤버와 함께 함수에 전달 할 수 있다는 것이다.

#include <iostream>
using namespace std;

struct Employee
{
    short id;
    int age;
    double wage;
};

void printInformation(Employee employee)
{
    cout << "ID: " << employee.id << endl;
    cout << "Age: " << employee.age << endl;
    cout << "Wage: " << employee.wage << endl;
}

int main()
{
    Employee joe = {14,32,24.15};
    Employee frank = {15,28,18.27};
    
    //print joe's information
    printInformation(joe);
    
    cout << endl;
    
    //print Frank's information
    printInformation(frank);
    
    return 0;
}

//program outputs:

ID: 14
Age: 32
Wage: 24.15

ID: 15
Age: 28
Wage: 18.27

또한 함수는 구조체를 반환함으로써 여러 변수를 반환할 수 있다.

#include <iostream>
using namespace std;

struct Point3d
{
    double x;
    double y;
    double z;
};

Point3d getZeroPoint()
{
    Point3d temp = {0.0, 0.0, 0.0};
    return temp;
}

int main()
{
    Point3d zero = getZeroPoint();
    
    if(zero.x == 0.0 && zero.y == 0.0 && zero.z == 0.0)
        cout << "The point is zero" << endl;
    else
        cout << "The point is not zero" <<endl;
        
    return 0;
}

//The point is zero

이런식으로 me를 받고 받아서 할 수 있다.&amp;nbsp;

또한 구조체 안에 함수를 넣을 수 있다.

 

구조체 안에 함수를 넣어주면 파라미터를 받을 필요도 없어진다

 

더이상 파라미터는 필요없다.

함수라는 것을 표현해주기 위해서 ()를 써줌.

출력 할 때 필요한 데이터들은 모두 같이 가지고 있기 때문에 데이터를 따로 넣어줄 필요가 없다.

 

대입도 가능하다

 

이렇게 해도 해준다

여기서 오해하지 말아야 할 것은 단순한 경우에만 해준다.

나중에 복잡한 프로그래밍 스킬을 발휘하기 시작하면 struct에 들어있는 멤버들이 복잡해진다.

멤버안에 사용자 정의 데이터형의 변수가 들어갈 수 도 있다.

그럴때는 (=) assignment 연산자가 operator 가 우리가 의도하지 않은 방식으로 작동할 수 있다.

 

int를 int로 복사하거나 float 를 float로 복사하는 건 쉽다

그런데 구조체를 구조체로 복사하는 것

클래스를 클래스로 복사하는 것은 조금 문제가 생길 수도 있다.

 

항상 대입연산자 assignment 연산자가 항상 내가 의도한대로 작동하진 않을 수도 있다는 것 기억하자

중첩된 구조체 (Nested structs)

구조체는 다른 구조체를 포함할 수 있다.

 

 

이렇게 표현 할 수도 있다.

사용자 정의 데이터 타입(user defined data type)을 마치 기본 데이터형처럼 사용하고 있다.

struct Employee
{
    short id;
    int age;
    double wage;
};

struct Company
{
    Employee CEO; //Employee is a struct within the Company struct
    int numberOfEmployees;
};

Company myCompany;

위 프로그램에서 CEO의 월급을 알고 싶으면 멤버 선택 연산자를 두번 사용하면 된다.

std::cout << myCompany.CEO.wage << std::endl;

중첩된 구조체에 대해 중첩된 초기화 목록을 사용할 수 있다.

struct Employee
{
    short id;
    int age;
    double wage;
};

struct Company
{
    Employee CEO; //Employee is a struct within the Company struct
    int numberOfEmployees;
};

Company myCompany = {{1,42,60000.0f},5};

구조체 크기와 정렬 (Structs size and data structure alignment)

일반적으로 구조체의 크기는 모든 멤버의 크기를 합한 것이지만 , 항상 그렇지만은 않다.

Employee 구조체를 보자.

short는 2바이트, int 4바이트, double 8바이트 이므로

Employee의 크기가 2 + 4 + 8 = 14바이트가 될 것으로 예상할 수 있다.

정확한 크기를 확인하려면 sizeof 연산자를 사용하면 된다.

 

14가 아닌 16이 나온다.

왜 14 가 아니라 16이 나올까

자료를 배치할 때 컴퓨터가 잘 처리할 수 있는 형태로 배치를 하다보면 2바이트가 아마 배치가 잘 안될것이다.

2 하고 빈칸인 2바이트가 있고 그 다음에 4바이트 8바이트 이렇게 배치가 된다.

성능상의 이유로 컴파일러는 때때로 구조에 간격을 추가한다.

 

2 가 추가되는 이것을 패딩(padding) 이라고 부른다.

 

 

Comments