프로그래밍 언어/C++

[자료형(data type)] C++ 파생형 구조체 총정리

순정법사 2023.09.13

A. 구조체

1. 구조체의 기본

a. 구조체란?

🌟 사용자가 C++언어의 기본 타입을 가지고 새롭게 정의할 수 있는 사용자 정의 타입

= 사용자 정의 자료형이라고 부름

 

배열이 같은 타입의 변수 집합이라면, 구조체는 다양한 타입의 변수 집합을 하나의 타입으로 나타낸 것

👉 이때 구조체를 구성하는 변수(함수)를 구조체의 맴버 또는 맴버변수라고 함

타입일 뿐만 아니라, 클래스의 기초가 됨

 

b. 구조체의 특징

기본 타입만으로는 나타낼 수 없는 복잡한 데이터를 표현할 수 있음

또한 함수 리턴값으로 구조체를 사용하면 여러 개의 데이터를 한 번에 반환할 수 있음 (함수는 원래 하나의 값만 가능)

 

c. 구조체의 정의와 선언

📘 구조체 타입 정의

struct 구조체이름	//struct 키워드 : 구조체의 시작을 알림
{
    멤버변수1의타입 멤버변수1의이름;
    멤버변수2의타입 멤버변수2의이름;
    ...
};	//; : 구조체의 정의를 종료한다는 의미

 

📘 구조체 변수 선언

struct 구조체이름 구조체변수이름;

⏳ 예제

struct Book web_book;

//C++에서는 구조체 변수를 선언할 때 struct 키워드를 생략가능

 

🎨 도식화

 

book이라는 이름의 구조체를 정의하는 그림

 

 

👉 구조체의 정의와 선언을 동시에 할수도 있음

 

📘 문법

struct 구조체이름
{
    멤버변수1의타입 멤버변수1의이름;
    멤버변수2의타입 멤버변수2의이름;
    ...
} 구조체변수이름;

🤓 예제

struct book
{
    char title[30];
    char author[30];
    int price;
} my_book;

 

d. 구조체 멤버로의 접근방법

🌟 멤버 참조 연산자(.)를 사용

 

구조체의 주소값과 구조체의 첫 번째 멤버 변수의 주소값은 언제나  같음

 

📘 문법

구조체변수이름.멤버변수이름

🤓 예제

my_book.author

 

e. 구조체 변수의 초기화

🌟 중괄호({})를 사용한 초기화 리스트를 사용하여 초기화

 

  • 멤버 변수가 정의된 순서에 따라 차례대로 초깃값이 설정
  • 초기화하지 않은 멤버 변수는 0으로 초기화
  • 구조체 변수를 초기화 할때 = 생략 가능
  • 💥 narrowing 지원하지 않음

 

📘 문법

구조체변수이름 = {멤버변수1의초깃값, 멤버변수2의초깃값, ...};

//ex) web_book = {"HTML과 CSS", "홍길동", 28000};

 

🤓 예제 : 배열의 초기화 방법을 사용한 초기화

#include <iostream>
#include <string>
using namespace std;

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원입니다.

 

 

 


B. 구조체의 활용

1. 함수와 구조체

함수의 매개변수나, 반환값으로 구조체를 사용할 수 있음

사용 방식은 기본 타입과 완전히 같고, 구조체를 가리키는 포인터나 멤버 변수만을 사용할 수도 있음

 

a. 매개변수 : 구조체를 인수로 전달하기

원본 구조체의 복사본을 가지고 작업하면 데이터가 안전해짐

 

⏳ 예제 : 구조체의 멤버 변수를 함수의 인수로 전달

#include <iostream>
using namespace std;

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원입니다.

 

b. 매개변수 : 구조체의 주소를 인수로 전달하기

구조체의 복사본이 아닌 주소 하나만을 전달하므로 빠르다는 장점

 

💥 하지만 원본 데이터 보호 측면에서는 위험함

 

⏳ 예제 : 함수의 인수로 구조체의 주소를 직접 전달

#include <iostream>
using namespace std;

struct Prop
{
	int savings;
	int loan;	
};

int CalcProperty(Prop*);

int main(void)
{
	int hong_prop;
	Prop hong =	{10000000, 4000000};
	
	hong_prop = CalcProperty(&hong); // 구조체의 주소를 함수의 인수로 전달함. 
	
	cout << "홍길동의 재산은 적금 " << hong.savings << "원에 대출 " << hong.loan
		<< "원을 제외한 총 " << hong_prop << "원입니다.";
	return 0;
}

int CalcProperty(Prop* money)
{
	money->savings = 100; // 호출된 함수에서 원본 구조체의 데이터를 변경
	return (money->savings - money->loan);
}

✨ 실행결과

홍길동의 재산은 적금 100원에 대출 4000000원을 제외한 총 -3999900원입니다.

 

👉 위와 같이 데이터가 수정되어버려서 const 키워드를 활용해야 함

 

⏳ 예제 : const 키워드를 사용해 수정을 방지

#include <iostream>
using namespace std;

struct Prop
{
	int savings;
	int loan;	
};

int CalcProperty(const Prop*);

int main(void)
{
	int hong_prop;
	Prop hong =	{10000000, 4000000};
    
	hong_prop = CalcProperty(&hong); // 구조체의 멤버 변수를 함수의 인수로 전달함 
	
	cout << "홍길동의 재산은 적금 " << hong.savings << "원에 대출 " << hong.loan 
		<< "원을 제외한 총 " << hong_prop << "원입니다.";
	return 0;
}


int CalcProperty(const Prop* money) // const 키워드를 사용하여 구조체의 데이터를 직접 수정하는 것을 방지함.
{
	//원본 구조체에 대한 수정을 시도할 경우 C++ 컴파일러는 오류를 발생
	//money->savings = 100; // 호출된 함수에서 원본 구조체의 데이터를 변경
	return (money->savings - money->loan);
}

✨ 실행결과

홍길동의 재산은 적금 10000000원에 대출 4000000원을 제외한 총 6000000원입니다.

 

💥 주석처리된 부분을 실행하면 C++ 컴파일러는 오류를 발생

 

c. 반환값 : 구조체

구조체를 사용하면 한 번에 여러개의 데이터를 반환할 수 있게 함

 

⏳ 예제 : 함수의 반환값으로 구조체를 반환

...

int main(void)
{
	Prop hong;
	int hong_prop;
	
	hong = InitProperty();	
    ...
}

Prop InitProperty(void)
{
	Prop hong_prop = {10000000, 4000000};
	return hong_prop; // 구조체를 함수의 반환값으로 반환함.
}

...

 

 

2. 중첩된 구조체

a. 구조체를 가지는 구조체

🌟 구조체를 정의할 때 멤버 변수로 또 다른 구조체를 포함할 수 있음 

 

⏳ 예제 : Address 구조체를 중첩하는 User 구조체

#include <iostream>
#include <string>

// 주소 정보를 저장하는 Address 구조체
struct Address {
    std::string street;
    std::string city;
    std::string zipCode;
};

// 사용자 정보를 저장하는 User 구조체 (주소 정보 포함)
struct User {
    std::string username;
    std::string email;
    Address address; // 중첩된 Address 구조체
};

int main() {
    // 사용자 정보 초기화
    User user;
    user.username = "johndoe";
    user.email = "johndoe@example.com";
    user.address.street = "123 Main St";
    user.address.city = "Anytown";
    user.address.zipCode = "12345";

    // 정보 출력
    std::cout << "Username: " << user.username << std::endl;
    std::cout << "Email: " << user.email << std::endl;
    std::cout << "Address: " << user.address.street << ", " << user.address.city << ", " << user.address.zipCode << std::endl;

    return 0;
}

✨ 실행결과

Username: johndoe
Email: johndoe@example.com
Address: 123 Main St, Anytown, 12345

 

 

3. 구조체의 크기

a. 구조체의 크기 계산하기

🌟 멤버 변수들의 크기에 따라 결정

 

💥 하지만 구조체의 크기가 언제나 멤버 변수들의 크기 총합과 일치하는 것은 아님

 

⏳ 예제 : 구조체의 크기 계산하기

#include <iostream>
using namespace std;

struct TypeSize
{
	char a;
	int b;
	double c;
};

int main(void)
{
	cout << "구조체 TypeSize의 각 멤버의 크기는 다음과 같습니다." << endl;
	cout << sizeof(char) << ", " << sizeof(int) << ", " << sizeof(double) << endl << endl;
	
	cout << "구조체 TypeSize의 크기는 다음과 같습니다." << endl;
	cout << sizeof(TypeSize);
	return 0;
}

✨ 실행결과

구조체 TypeSize의 각 멤버의 크기는 다음과 같습니다.
1, 4, 8

구조체 TypeSize의 크기는 다음과 같습니다.
16	//위와 크기가 일치하지 않음

 

b. 바이트 패딩(byte padding)

🌟 구조체를 메모리에 할당할 때 컴파일러가 프로그램의 속도 향상을 위해 사용하는 규칙

 

구조체는 다양한 타입을 멤버 변수로 가질 수 있어서

컴파일러는 메모리의 접근을 쉽게 하기 위해 가장 큰 멤버 변수를 기준으로 메모리 크기를 맞추게 됨

이걸 바이트 패딩이라고 하고, 이때 추가되는 바이트를 패딩 바이트(padding byte)라고 함

 

 

  1. 가장 큰 double 자료형으로 8byte 할당
  2. char, int형 메모리 처리 
  3. double형 메모리 처리할 공간이 없으므로 8byte 추가 할당
  4. 최종적으로 16byte 메모리가 할당됨 (3 패딩 바이트)

 

 

 


출처 : http://www.tcpschool.com/cpp/cpp_struct_intro

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com