하지만 구조체에서 구조체 멤버로 접근하려고 할 때는 .(멤버 참조 연산자)를 사용해야 한다.
구조체변수이름.멤버변수이름
web_book.author
3) 구조체 변수의 초기화
C++에서 구조체 변수는 {}(중괄호)를 사용한 초기화 리스트를 사용하여 초기화한다.
구조체변수이름={멤버변수1의초깃값,멤버변수2의초깃값,...};
web_book={"HTML과 CSS","홍길동",28000};
이때 구조체 정의에서 멤버 변수가 정의된 순서에 따라 차례대로 초깃값이 설정되며, 나머지 멤버 변수는 0으로 초기화된다.
#include<iostream>#include<string>usingnamespacestd;structbook{stringtitle;stringauthor;intprice;};intmain(){bookweb_book={"HTML과 CSS","홍길동",28000};bookjava_book={"Java language","이순신"};cout<<"첫 번째 책의 제목은 "<<web_book.title<<"이고, 저자는 "<<web_book.author<<"이며, 가격은 "<<web_book.price<<"원입니다."<<endl;cout<<"두 번째 책의 제목은 "<<java_book.title<<"이고, 저자는 "<<java_book.author<<"이며, 가격은 "<<java_book.price<<"원입니다."<<endl;return0;}// 첫 번째 책의 제목은 HTML과 CSS이고, 저자는 홍길동이며, 가격은 28000원입니다.// 두 번째 책의 제목은 Java language이고, 저자는 이순신이며, 가격은 0원입니다.
C++11부터는 구조체 변수를 초기화할 때에 =(대입 연산자)를 생략할 수 있으나, Narrowing은 지원하지 않는다.
3. 구조체의 활용
1) 함수와 구조체
C++에서는 함수를 호출할 때 전달되는 인수나, 함수가 종료될 때 반환되는 반환값으로도 구조체를 사용할 수 있다.
그 방식은 기본 타입과 완전히 같으며, 구조체를 가리키는 포인터나 구조체의 한 멤버 변수만을 사용할 수도 있다.
다음의 코드는 구조체의 멤버 변수를 함수의 인수로 전달하는 것이다.
#include<iostream>usingnamespacestd;structProp{intsavings;intloan;};intCalcProperty(int,int);intmain(void){inthong_prop;Prophong={10000000,4000000};hong_prop=CalcProperty(hong.savings,hong.loan);// 구조체의 멤버 변수를 함수의 인수로 전달함cout<<"홍길동의 재산은 적금 "<<hong.savings<<"원에 대출 "<<hong.loan<<"원을 제외한 총 "<<hong_prop<<"원입니다."<<endl;return0;}intCalcProperty(ints,intl){return(s-l);}// 홍길동의 재산은 적금 10000000원에 대출 4000000원을 제외한 총 6000000원입니다.
위와 같이 구조체를 인수로 전달하는 방식은 함수가 원본 구조체의 복사본을 가지고 작업하므로 안전하다는 장점을 가진다.
다음의 코드는 함수의 인수로 구조체의 주소를 직접 전달하는 것이다.
#include<iostream>usingnamespacestd;structProp{intsavings;intloan;};intCalcProperty(Prop*);intmain(void){inthong_prop;Prophong={10000000,4000000};hong_prop=CalcProperty(&hong);// 구조체의 주소를 함수의 인수로 전달함cout<<"홍길동의 재산은 적금 "<<hong.savings<<"원에 대출 "<<hong.loan<<"원을 제외한 총 "<<hong_prop<<"원입니다."<<endl;return0;}intCalcProperty(Prop*money){money->savings=100;// 호출된 함수에서 원본 구조체의 데이터를 변경return(money->savings-money->loan);}// 홍길동의 재산은 적금 100원에 대출 4000000원을 제외한 총 -3999900원입니다.
위와 같이 구조체를 가리키는 포인터를 인수로 전달하는 방식은 구조체의 복사본이 아닌 주소 하나만을 전달하므로 빠르다는 장점을 가진다.
하지만 호출된 함수에서 원본 구조체에 직접 접근하므로, 원본 데이터의 보호 측면에서는 매우 위험하다.
따라서 다음 코드의 CalcProperty() 함수처럼 const 키워드를 사용하여 함수에 전달된 인수를 함수 내에서는 직접 수정할 수 없도록 하는 것이 좋다.
#include<iostream>usingnamespacestd;structProp{intsavings;intloan;};PropInitProperty(void);intCalcProperty(constProp*);intmain(void){inthong_prop;Prophong;hong=InitProperty();hong_prop=CalcProperty(&hong);// 구조체의 멤버 변수를 함수의 인수로 전달함cout<<"홍길동의 재산은 적금 "<<hong.savings<<"원에 대출 "<<hong.loan<<"원을 제외한 총 "<<hong_prop<<"원입니다."<<endl;return0;}PropInitProperty(void){Prophong_prop={10000000,4000000};returnhong_prop;// 구조체를 함수의 반환값으로 반환함}intCalcProperty(constProp*money){// const 키워드를 사용하여 구조체의 데이터를 직접 수정하는 것을 방지함// money->savings = 100; // 호출된 함수에서 원본 구조체의 데이터를 변경. 컴파일 오류 발생return(money->savings-money->loan);}// 홍길동의 재산은 적금 10000000원에 대출 4000000원을 제외한 총 6000000원입니다.
위의 코드에서 CalcProperty() 함수 내의 주석 처리된 부분을 실행하여 원본 구조체에 대한 수정을 시도할 경우 C++ 컴파일러는 오류를 발생시킨다.
또한, 위의 코드에서 InitProperty() 함수의 반환값으로 구조체를 반환한다.
기본적으로 C++의 함수는 한 번에 하나의 데이터만을 반환할 수 있다.
하지만 이렇게 구조체를 사용하면 한 번에 여러 개의 데이터를 반환할 수 있게 된다.
2) 중첩된 구조체
C++에서는 구조체를 정의할 때 멤버 변수로 또 다른 구조체를 포함할 수 있다.
#include<iostream>#include<string>usingnamespacestd;structName{stringfirst;stringlast;};structFriends{Namefirts_name;stringaddress;stringjob;};intmain(void){// 구조체 변수 선언과 동시에 초기화Friendshong={.firts_name.first="길동",.firts_name.last="홍",.address="서울시 강남구 역삼동",.job="학생"};// 구조체 변수 선언 후 초기화 리스트 사용// Friends hong;// hong = {{"길동", "홍"}, "서울시 강남구 역삼동", "학생"};cout<<hong.address<<endl<<endl<<hong.firts_name.last<<hong.firts_name.first<<"에게,"<<endl<<"그동안 잘 지냈니? 아직도 "<<hong.job<<"이니?"<<endl<<"다음에 꼭 한 번 보자."<<endl<<"잘 지내."<<endl;return0;}// 서울시 강남구 역삼동//// 홍길동에게,// 그동안 잘 지냈니? 아직도 학생이니?// 다음에 꼭 한 번 보자.// 잘 지내.
위의 코드에서 Friends 구조체는 Name 구조체를 멤버 변수로 포함하고 있다.
3) 구조체의 크기
구조체의 크기는 멤버 변수들의 크기에 따라 결정된다.
하지만 구조체의 크기가 언제나 멤버 변수들의 크기 총합과 일치하는 것은 아니다.
#include<iostream>usingnamespacestd;structTypeSize{chara;intb;doublec;};intmain(void){cout<<"구조체 TypeSize의 각 멤버의 크기는 다음과 같습니다."<<endl;cout<<sizeof(char)<<", "<<sizeof(int)<<", "<<sizeof(double)<<endl;cout<<"구조체 TypeSize의 크기는 다음과 같습니다."<<endl;cout<<sizeof(TypeSize)<<endl;return0;}// 구조체 TypeSize의 각 멤버의 크기는 다음과 같습니다.// 1, 4, 8// 구조체 TypeSize의 크기는 다음과 같습니다.// 16
위의 코드에서 구조체 멤버 변수의 크기는 각각 1, 4, 8바이트이다.
하지만 구조체의 크기는 멤버 변수들의 크기 총합인 13바이트가 아니라 16바이트가 된다.
구조체를 메모리에 할당할 때 컴파일러는 프로그램의 속도 향상을 위해 바이트 패딩이라는 규칙을 이용한다.
구조체는 다양한 크기의 타입을 멤버 변수로 가질 수 있는 타입이다.
하지만 컴파일러는 메모리의 접근을 쉽게 하려고 크기가 가장 큰 멤버 변수를 기준으로 모든 멤버 변수의 메모리 크기를 맞추게 된다.
이것을 바이트 패딩이라고 하며, 이때 추가되는 바이트를 패딩 바이트라고 한다.
위의 코드에서는 크기가 가장 큰 double 타입의 크기인 8바이트가 기준이 된다.
맨 처음 char 타입 멤버 변수를 위해 8바이트가 할당되며, 할당되는 1바이트를 제외한 7바이트가 남게 된다.
그다음 int 타입 멤버 변수는 남은 7바이트보다 작으므로, 그대로 7바이트 중 4바이트를 할당하고 3바이트가 남게 된다.
마지막 double 타입 멤버 변수는 8바이트인데 남은 공간은 3바이트뿐이므로 다시 8바이트를 할당 받는다.
따라서 이 구조체의 크기는 총 16바이트가 되며, 그중에서 패딩 바이트는 3바이트가 된다.
4. 공용체와 열거체
1) 공용체
공용체는 union 키워드를 사용하여 선언하며, 모든 면에서 구조체와 같다.
하지만 모든 멤버 변수가 하나의 메모리 공간을 공유한다는 점만이 다르다.
모든 멤버 변수가 같은 메모리를 공유하기 때문에 공용체는 한 번에 하나의 멤버 변수밖에 사용할 수 없다.
공용체는 순서가 규칙적이지 않고, 미리 알 수 없는 다양한 타입의 데이터를 저장할 수 있도록 설계된 타입이다.
이러한 공용체는 크기가 가장 큰 멤버 변수의 크기로 메모리를 할당 받는다.
따라서 공용체 배열을 사용하면, 같은 크기로 구성된 배열 요소에 다양한 크기의 데이터를 저장할 수 있다.
다음의 코드는 공용체의 한 멤버 변수만을 초기화하면, 나머지 멤버 변수들도 모두 같은 데이터를 공유한다는 것을 보여준다.