2022. 6. 17. 12:39ㆍ♣ C++
이번 포스팅에선 구조체에 대해 알아보도록 하겠습니다.
구조체 : 사용자 정의 타입
다양한 타입의 변수 집합을 하나의 타입으로 나타내는 것이 구조체입니다.
예를 들어, 책 제목/ 저자/ 가격의 집합을 하나의 book 타입으로 정의하는 것이 구조체입니다.
개발 환경 : VSCode, Windows 10
기본 개발 세팅은 다음과 같습니다.
#include <iostream>
#include <string.h>
#include <string>
using namespace std;
int main()
{
return 0;
}
📌 구조체 선언하기
구조체는 main 함수 밖에서 선언합니다.
이번 포스팅에선 Book이란 이름으로 선언하도록 하겠습니다.
struct 구조체이름 {};
Book 구조체 선언
struct Book
{
char title[30];
char author[20];
int price;
int year;
};
int main()
{
return 0;
}
Book 구조체 안에는 title, author 문자열과 price, year 정수형 데이터가 들어갈 수 있습니다.
📌 구조체 변수 선언 & 초기화
구조체를 선언했다면, 그 타입의 변수를 선언해야 합니다.
struct 구조체이름 변수이름;
struct 구조체이름 변수이름 = {내용물};
2권의 소설을 변수로 선언하고, 초기화해보았습니다.
struct Book
{
char title[30];
char author[20];
int price;
int year;
};
int main()
{
struct Book fiction = {"kangmong", "kinger", 12000, 1940};
struct Book fiction_2 = {"love is fair", "mong", 12000};
cout << "소설의 이름은 " << fiction.title << ", 소설의 작가는 " << fiction.author << ", 소설의 가격은 " << fiction.price << ", 소설의 발행년도는 " << fiction.year << " 입니다." << endl;
cout << "소설의 이름은 " << fiction_2.title << ", 소설의 작가는 " << fiction_2.author << ", 소설의 가격은 " << fiction_2.price << ", 소설의 발행년도는 " << fiction_2.year << " 입니다." << endl;
return 0;
}
결과
소설의 이름은 kangmong, 소설의 작가는 kinger, 소설의 가격은 12000, 소설의 발행년도는 1940 입니다.
소설의 이름은 love is fair, 소설의 작가는 mong, 소설의 가격은 12000, 소설의 발행년도는 0 입니다.
💥 주의해야 할 점
구조체 변수를 초기화한다고 생각해봅시다. fiction_2처럼 가격까지만 초기화한 경우, 나머지 값인 year에 대해서는 자동으로 0으로 초기화됩니다. 이점을 숙지해야 합니다.
📌 구조체 활용
1. 함수와 구조체
함수의 인수로 구조체를 전달할 수 있습니다.
전달받은 인수를 빼는 함수를 구현하고, 구조체를 전달해봅시다.
struct Book
{
char title[30];
char author[20];
int price;
int year;
};
int Calculator(int,int);
main 함수를 호출하기 전, struct 구조체를 선언하고 함수 원형을 선언했습니다.
int main()
{
struct Book fiction = {"kangmong", "kinger", 12000, 1940};
struct Book fiction_2 = {"love is fair", "mong", 10500};
int CalcPrice;
CalcPrice = Calculator(fiction.price, fiction_2.price);
cout << "두 소설책의 값 차이는 " << CalcPrice << " 입니다." << endl;
return 0;
}
int Calculator(int a, int b){
return a - b;
}
CalcPrice 변수를 만들고, 빼기 함수의 결과 값을 받도록 했습니다.
결과
두 소설책의 값 차이는 1500 입니다.
구조체를 함수의 인수로 넣을 수 있음을 확인했습니다.
다만, 구조체를 전달하는 것보다 구조체의 주소를 직접 전달하는 것이 함수의 속도를 더 높일 것입니다.
구조체를 전달하면 컴퓨터가 다시 구조체의 주소를 (1) 찾아서 (2) 접근하지만, 구조체의 주소를 전달하면 (1) 바로 접근만 하면 되기 때문입니다.
2. 함수의 인수로 구조체의 주소를 직접 전달하기
실행 속도를 더 높이기 위해 구조체의 주소를 직접 전달해봅시다.
struct Book
{
char title[30];
char author[20];
int price;
int year;
};
int Calculator(Book*, Book*);
이번엔 구조체 포인터를 직접 인수로 전달합니다.
int main()
{
struct Book fiction = {"kangmong", "kinger", 12000, 1940};
struct Book fiction_2 = {"love is fair", "mong", 10500};
int CalcPrice;
CalcPrice = Calculator(&fiction, &fiction_2);
cout << "두 소설책의 값 차이는 " << CalcPrice << " 입니다." << endl;
return 0;
}
int Calculator(Book* fiction1, Book* fiction2){
return (fiction1->price - fiction2->price);
}
구조체 변수 fiction과 fiction_2의 주소 값을 Calculator의 인수로 전달합니다.
밑의 Calculator는 인수를 받아서 계산하고 있는 것을 볼 수 있습니다.
결과
두 소설책의 값 차이는 1500 입니다.
3. 함수 내에서는 인수를 수정할 수 없도록 하기
위의 방법으로 함수를 운용하는 경우, 언제든 함수에 전달된 인수가 함수 안에서 바뀔 수 있다는 가능성을 가집니다.
struct Book
{
char title[30];
char author[20];
int price;
int year;
};
int Calculator(Book*, Book*);
int main()
{
struct Book fiction = {"kangmong", "kinger", 12000, 1940};
struct Book fiction_2 = {"love is fair", "mong", 10500};
int CalcPrice;
CalcPrice = Calculator(&fiction, &fiction_2);
cout << "두 소설책의 값 차이는 " << CalcPrice << " 입니다." << endl;
return 0;
}
int Calculator(Book* fiction1, Book* fiction2){
fiction1 -> price = 20000;
return (fiction1->price - fiction2->price);
}
밑의 Calculator에서 fiction1의 price를 20000원으로 변경할 경우, 변경된 그대로 계산이 진행됩니다.
결과
두 소설책의 값 차이는 9500 입니다.
이런 가능성을 없애는 방법은 Calculator 함수가 상수 인수(const 변수)를 받는 것입니다.
struct Book
{
char title[30];
char author[20];
int price;
int year;
};
int Calculator(const Book*, const Book*);
int main()
{
struct Book fiction = {"kangmong", "kinger", 12000, 1940};
struct Book fiction_2 = {"love is fair", "mong", 10500};
int CalcPrice;
CalcPrice = Calculator(&fiction, &fiction_2);
cout << "두 소설책의 값 차이는 " << CalcPrice << " 입니다." << endl;
return 0;
}
int Calculator(const Book* fiction1, const Book* fiction2){
fiction1 -> price = 20000;
return (fiction1->price - fiction2->price);
}
이렇게 코드를 입력하고, fiction1 -> price = 20000; 을 작성하면 다음과 같은 오류가 뜹니다.
식이 수정할 수 있는 lvalue여야 합니다.
이처럼 const를 이용하여 상수 인수를 전달하면 함수 내에선 변경할 수 없습니다.
📌 중첩된 구조체
구조체는 멤버 변수로 또 다른 구조체를 포함할 수 있습니다.
Book 구조체를 선언함과 동시에, 그 멤버 변수들 중 하나인 author_name 구조체도 선언했습니다.
struct author_name
{
char first[10];
char last[20];
};
struct Book
{
char title[30];
author_name name;
int price;
int year;
};
중첩된 구조체를 사용하는 방법은 간단합니다.
구조체.멤버 구조체.멤버 구조체의 멤버 변수
예시
int main()
{
struct Book fiction = {
"kangmong",
{"kang", "mongo"},
12000,
1940
};
cout << "소설 kangmong의 저자는 " << fiction.name.first << fiction.name.last << " 입니다." << endl;
return 0;
}
fiction.name.first를 통해 fiction 안의 name 구조체에서 first라는 매개 변수를 가져온 것을 알 수 있습니다.
결과
소설 kangmong의 저자는 kangmongo 입니다.
📌 구조체의 크기
구조체의 크기는 기본적으로 멤버 변수에 제일 큰 영향을 받습니다.
하지만 언제나 멤버 변수의 합=구조체 크기는 아닙니다.
구조체 Book의 크기를 살펴보겠습니다.
struct author_name
{
char first[10];
char last[20];
};
struct Book
{
char title[30];
author_name name;
int price;
int year;
};
int형이 2개, char형이 1개, author_name형이 1개입니다. 이들의 합은 다음과 같습니다.
int main()
{
cout << "int = " << sizeof(int) << ", " << "char = " << sizeof(char) << ", " << "author_name = " << sizeof(author_name) << endl;
cout << "멤버 변수들의 합 : " << sizeof(int) + sizeof(int) + sizeof(char) + sizeof(author_name) << endl;
return 0;
}
결과
int = 4, char = 1, author_name = 30
멤버 변수들의 합 : 39
하지만 Book 구조체의 크기를 보면 어떨까요?
int main()
{
cout << "Book 구조체의 크기 : " << sizeof(Book);
return 0;
}
결과
Book 구조체의 크기 : 68
이렇게 서로의 결과가 다르게 나오는 것은 바이트 패딩 규칙 때문입니다.
바이트 패딩 규칙에 대해선 추후에 다루도록 하겠습니다. 다만, 구조체 멤버 변수들의 합이 무조건 구조체의 크기는 아니라는 점을 알아두시길 바랍니다.
📌 공용체와 열거체
마지막으로 살펴볼 구조체들은 공용체와 열거체입니다.
간단하게 보고 가겠습니다.
🎨 공용체
모든 멤버 변수가 하나의 메모리 공간을 공유합니다. 다양한 타입의 데이터를 저장할 수 있습니다. 하나의 멤버 변수만 초기화하면, 나머지 멤버 변수들도 같은 데이터를 공유합니다.
공용체는 union으로 선언하며, 멤버 변수 앞에 unsigned를 붙여줍니다.
union ShareData
{
unsigned char a;
unsigned short b;
unsigned int c;
};
int main()
{
ShareData var;
var.c = 0x12345678;
cout << hex;
cout << var.a << endl;
cout << var.b << endl;
cout << var.c << endl << endl;
return 0;
}
var 변수를 선언한 후, 0x12345678로 초기화합니다.
cout << hex; 명령어를 사용하여 16진수로 바꾼 후에, var 구조체의 멤버 변수들을 불러옵니다.
결과
x
5678
12345678
각각의 멤버 변수들이 0x12345678에서 각각에 맞는 수와 문자열을 가져온 것을 확인할 수 있습니다.
이렇듯 공용체는 하나의 메모리 공간에서 각각의 멤버 변수가 자신의 것들을 가져오는 형태를 취합니다.
🎨 열거체
새로운 타입을 선언하면서 동시에 그 타입이 가질 수 있는 정수형 상수값도 같이 명시합니다.
가독성을 높이고, 변수에 의미를 부여하기 위해서 사용됩니다.
열거체는 enum으로 선언되며, 초기화할 때 정수형 상수값을 같이 부여합니다.
다음은 임의로 선언한 Weather 열거체입니다.
enum Weather {SUNNY = 0, CLOUD = 10, RAIN = 20, SNOW = 30};
열거체를 활용한 코드입니다.
int main()
{
int input;
Weather today_weather;
cout << "오늘의 날씨를 입력해주세요 : " << endl;
cout << "(SUNNY = 0, CLOUD = 10, RAIN = 20, SNOW = 30)\n" << endl;
cin >> input;
today_weather = (Weather)input;
return 0;
}
먼저 today_weather 열거체를 선언해줍니다.
input을 통해 정수 혹은 문자열을 전달받고, today_weather에 대입합니다.
int main()
{
int input;
Weather today_weather;
cout << "오늘의 날씨를 입력해주세요 : " << endl;
cout << "(SUNNY = 0, CLOUD = 10, RAIN = 20, SNOW = 30)\n" << endl;
cin >> input;
today_weather = (Weather)input;
switch (today_weather)
{
case SUNNY :
cout << "오늘의 날씨는 끝내줘요!";
break;
case CLOUD :
cout << "오늘의 날씨는 선선해요!";
break;
case RAIN :
cout << "오늘의 날씨는 운치있어요!";
break;
case SNOW :
cout << "오늘의 날씨는 신나요!";
break;
default:
cout << "정확한 상숫값을 입력해주세요.";
break;
}
cout << endl << "열거체 Weather의 각 상숫값은 " << SUNNY << ", " << CLOUD << ", "
<< RAIN << ", " << SNOW << "입니다.";
return 0;
}
today_weather에 입력된 데이터를 토대로 switch문을 돌려서 알맞은 데이터가 나오도록 합니다.
결과
오늘의 날씨를 입력해주세요 :
(SUNNY = 0, CLOUD = 10, RAIN = 20, SNOW = 30)
0
오늘의 날씨는 끝내줘요!
열거체 Weather의 각 상숫값은 0, 10, 20, 30입니다.
날씨를 입력하라는 명령에 0을 입력했고, 그에 따라 SUNNY가 반환되었습니다.
그에 맞는 문자열이 출력된 것을 볼 수 있습니다.
지금까지 구조체에 대해 알아보았습니다.
구조체 선언, 구조체 변수 선언 및 초기화, 크기, 공용체와 열거체 등 많은 개념을 학습한 포스팅입니다.
그러니 복습을 필수적으로 하고 다음 단계로 넘어가시길 바랍니다.
고생하셨습니다.
'♣ C++' 카테고리의 다른 글
[C++] 함수 포인터 표기를 단순화하는 방법 (0) | 2022.06.23 |
---|---|
[C++] 함수 Basic - 정의, 인수 전달, 포인터 (0) | 2022.06.23 |
[C++] string 클래스와 메소드 (0) | 2022.06.16 |
[C++] 메모리 동적 할당이 뭐에요? (0) | 2022.06.15 |
[C++] Pointer 개념, 연산에 대해 알기 (0) | 2022.06.13 |