1) 구조체란?
구조체는 기본 타입을 활용해 새롭게 정의할 수 있는 사용자 정의 타입이다.
배열이 같은 타입의 변수 집합이라면, 구조체는 다양한 타입의 변수 집합을 하나의 타입으로 나타낸 것이다.
구조체를 구성하는 변수를 구조체의 멤버 혹은 멤버 변수라고 한다.
구조체의 선언
struct 구조체이름
{
멤버변수1의타입 멤버변수1의이름;
멤버변수2의타입 멤버변수2의이름;
...
};
아래의 그림은 book 구조체를 선언하는 과정을 나타낸 것이다.

이렇게 선언된 구조체 타입은 구조체 변수를 선언할 수 있게 해준다.
선언
struct 구조체이름 구조체변수이름;
struct Book web_book;
구조체 멤버로서의 접근 방법
배열에서는 인덱스를 사용해서 배열 요소에 접근할 수 있다.
반면 구조체에서 구조체 멤버에 접근하기 위해서는 멤버 참조 연산자(.)를 사용해야 한다.
문법
구조체변수이름.멤버변수이름
web_book.author
구조체에의 멤버 변수에 접근하기 위해. 을 사용한 것을 확인할 수 있다.
구조체 변수의 초기화
구조체 변수는 중괄호 {} 를 사용한 초기화 리스트를 사용해서 초기화할 수 있다.
문법
구조체변수이름 = {멤버변수1의초깃값, 멤버변수2의초깃값, ...};
web_book = {"HTML과 CSS", "홍길동", 28000};
struct book
{
string title;
string author;
int price;
};
int main(void)
{
book web_book = {"HTML과 CSS", "홍길동", 28000};
book java_book = {"Java language", "이순신"};
cout << "첫 번째 책의 제목은 " << web_book.title << "이고, 저자는 " << web_book.author
<< "이며, 가격은 " << web_book.price << "원입니다." << endl;
cout << "두 번째 책의 제목은 " << java_book.title << "이고, 저자는 " << java_book.author
<< "이며, 가격은 " << java_book.price << "원입니다.";
return 0;
}
첫 번째 책의 제목은 HTML과 CSS이고, 저자는 홍길동이며, 가격은 28000원입니다.
두 번째 책의 제목은 Java language이고, 저자는 이순신이며, 가격은 0원입니다.
title, author, price를 각각의 값으로 초기화한 것을 확인할 수 있다.
2) 구조체의 활용
함수와 구조체
C++에서는 함수를 호출할 때 전달되는 인수나, 함수가 종료될 때 반환하는 값으로도 구조체를 사용할 수 있다.
struct Prop
{
int savings;
int loan;
};
int CalcProperty(int, int);
int main(void)
{
int hong_prop;
Prop hong = {10000000, 4000000};
hong_prop = CalcProperty(hong.savings, hong.loan); // 구조체의 멤버 변수를 함수의 인수로 전달함
cout << "홍길동의 재산은 적금 " << hong.savings << "원에 대출 " << hong.loan
<< "원을 제외한 총 " << hong_prop << "원입니다.";
return 0;
}
int CalcProperty(int s, int l)
{
return (s - l);
}
홍길동의 재산은 적금 10000000원에 대출 4000000원을 제외한 총 6000000원입니다.
위와 같이 구조체의 멤버 변수를 함수의 인수로 전달하는 방식은 함수가 원본 구조체의 복사본을 가지고 작업하기에 안전하다는 장점을 갖는다.
아래의 예제는 함수의 인수로 구조체의 주소를 직접 전달하는 예제이다.
int CalcProperty(Prop*);
int main(void)
{
...
hong_prop = CalcProperty(&hong); // 구조체의 주소를 함수의 인수로 전달함.
...
}
int CalcProperty(Prop* money)
{
money->savings = 100; // 호출된 함수에서 원본 구조체의 데이터를 변경
return (money->savings - money->loan);
}
홍길동의 재산은 적금 100원에 대출 4000000원을 제외한 총 -3999900원입니다.
위의 코드에서처럼 구조체를 가리키는 포인터를 인수로 전달하는 방식은 구조체의 복사본이 아닌 주소 하나만을 전달하기에 빠르다는 장점이 있다. 하지만 호출된 함수에서 원본 구조체에 직접 접근하므로, 원본 데이터의 보호 측면에서는 위험하다는 단점이 있다.
이러한 단점을 해결할 방법이 있는데, 바로 const 키워드를 사용하는 것이다.
Prop InitProperty(void);
int CalcProperty(const Prop*);
int main(void)
{
...
hong = InitProperty();
hong_prop = CalcProperty(&hong); // 구조체의 멤버 변수를 함수의 인수로 전달함
...
}
Prop InitProperty(void)
{
Prop hong_prop = {10000000, 4000000};
return hong_prop; // 구조체를 함수의 반환값으로 반환함.
}
int CalcProperty(const Prop* money) // const 키워드를 사용하여 구조체의 데이터를 직접 수정하는 것을 방지함.
{
//money->savings = 100; // 호출된 함수에서 원본 구조체의 데이터를 변경
return (money->savings - money->loan);
}
홍길동의 재산은 적금 10000000원에 대출 4000000원을 제외한 총 6000000원입니다.
const 키워드를 사용하면 내부의 값을 변경할 수 없다.
중첩된 구조체
C++에서는 구조체를 정의할 때 멤버 변수로 또 다른 구조체를 포함할 수 있다.
struct Name
{
string first;
string last;
};
struct Friends
{
Name first_name;
string address;
string job;
};
서울시 강남구 역삼동
홍길동에게,
그동안 잘 지냈니? 아직도 학생이니?
다음에 꼭 한번 보자.
잘 지내.
위의 예제를 보면 Friends 구조체는 Name 구조체를 멤버 변수로 포함하고 있음을 알 수 있다.
구조체의 크기
구조체의 크기는 멤버 변수들의 크기에 따라 결정되는데, 구조체의 크기가 언제나 멤버 변수들의 크기의 총합과 일치하는 것은 아니다.
struct TypeSize
{
char a;
int b;
double c;
};
int main(void)
{
cout << "구조체 TypeSize의 각 멤버의 크기는 다음과 같습니다." << endl;
cout << sizeof(char) << ", " << sizeof(int) << ", " << sizeof(double) << endl;
cout << "구조체 TypeSize의 크기는 다음과 같습니다." << endl;
cout << sizeof(TypeSize);
return 0;
}
구조체 TypeSize의 각 멤버의 크기는 다음과 같습니다.
1, 4, 8
구조체 TypeSize의 크기는 다음과 같습니다.
16
위의 예제에서 구조체 멤버 변수의 크기는 각각 1, 4, 8 바이트이다.
하지만 구조체의 크기를 출력했을 때 총합인 1+4+8의 총합인 13이 나오지 않고 16바이트가 출력됨을 알 수 있다.
왜일까?
구조체를 메모리에 할당할 때 컴파일러는 속도 향상을 위해 바이트 패딩(byte padding) 이라는 규칙을 이용한다. 구조체의 경우 다양한 크기의 타입을 멤버 변수로 갖는 타입인데, 컴파일러는 메모리의 접근을 쉽게 하기 위해 크기가 가장 큰 멤버 변수를 기준으로 모든 멤버 변수의 메모리 크기를 맞추게 되는 것이다.
이를 바이트 패딩이라고 하며, 이때 추가되는 바이트를 패딩 바이트(padding byte)라고 한다. 위의 예제에서는 패딩 바이트가 3인 것을 알 수 있다.

위의 예제에서는 크기가 가장 큰 double형 타입의 크기인 8바이트가 기준이 될 것이다.
맨 처음 char형 멤버 변수 선언을 위해 8바이트가 해당이 되는데 char. 즉 1바이트를 제외한 7바이트가 남게 된다. 그 다음 int형 변수는 4바이트이기에 남은 7바이트보다 작은 것을 알 수 있다. 그러면 char와 int 까지 바이트를 할당하고도 3바이트가 남게된 것이다. 마지막 double형 멤버 변수는 8바이트인데 남은 공간이 3바이트뿐이므로 다시 8바이트를 할당받게 된다. 이러한 과정에 의해 구조체의 크기는 총 16바이트가 되며 남은 3바이트가 패딩 바이트가 되는 것이다.
3) 공용체와 열거체
공용체(union)
공용체는 union 키워드를 사용하여 선언하며 모든 면에서 구조체와 동일하다.
하지만 모든 멤버 변수가 하나의 메모리 공간을 공유한다는 점만 다르다. 모든 멤버 변수가 하나의 같은 메모리 공간을 공유하므로 공용체(union)는 한 번에 하나의 멤버 변수밖에 사용할 수 없다.


공용체는 구조체와 달리 순서가 규칙적이지 않고, 미리 알 수 없는 다양한 타입의 데이터를 저장할 수 있도록 설계된 타입이다. 이러한 공용체는 크기가 가장 큰 멤버 변수의 크기로 메모리를 할당받는다.
따라서 공용체 배열을 사용하면 같은 크기로 구성된 배열 요소에 대해 다양한 크기의 데이터를 저장할 수 있다.
아래의 예제는 공용체의 한 개의 멤버 변수만 초기화하면, 나머지 멤버 변수들도 모두 같은 데이터를 공유한다는 것을 알 수 있다.
union ShareData
{
unsigned char a;
unsigned short b;
unsigned int c;
};
int main(void)
{
ShareData var;
var.c = 0x12345678;
cout << hex;
cout << var.a << endl;
cout << var.b << endl;
cout << var.c;
return 0;
}
x
5678
12345678
공용체에 저장된 값의 의미는 값을 저장할 때 공용체의 어떤 멤버 변수를 사용했는지에 따라 달리 해석된다.
따라서 공용체가 어떤 멤버 변수를 사용하여 저장했는지를 별도의 변수에 저장하여, 사용할 때에도 같은 멤버 변수를 사용해야 한다.
열거체(enumerated types)
열거체는 새로운 타입을 선언하면서 동시에 그 타입이 가질 수 있는 정수형 상숫값도 같이 명시한다.
이러한 열거체를 이용하면 프로그램의 가독성이 높아지고, 변수가 갖는 값에 의미를 부여할 수 있다.
enum Weather {SUNNY = 0, CLOUD = 10, RAIN = 20, SNOW = 30};
int main(void)
{
int input;
Weather wt;
cout << "오늘의 날씨를 입력해 주세요 : " << endl;
cout << "(SUNNY=0, CLOUD=10, RAIN=20, SNOW=30)" << endl;
cin >> input;
wt = (Weather)input;
switch (wt)
{
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;
}
오늘의 날씨를 입력해 주세요 :
(SUNNY=0, CLOUD=10, RAIN=20, SNOW=30)
SUNNY
오늘의 날씨는 아주 맑아요!
열거체 Weather의 각 상숫값은 0, 10, 20, 30입니다.
오늘의 날씨를 입력해 주세요 :
(SUNNY=0, CLOUD=10, RAIN=20, SNOW=30)
0
오늘의 날씨는 아주 맑아요!
열거체 Weather의 각 상숫값은 0, 10, 20, 30입니다.
'Computer Science > C++' 카테고리의 다른 글
| [C++] C++ 함수 (1) | 2023.12.02 |
|---|---|
| [C++] 함수의 기본 (1) | 2023.11.19 |
| [C++] 문자열 (1) | 2023.11.12 |
| [C++] 배열과 포인터(포인터의 개념,포인터의 연산, 메모리의 동적 할당) (0) | 2023.11.10 |
| [C++] 배열과 포인터(1차원 배열,다차원 배열) (0) | 2023.11.05 |