wn42
코딩이랑 이것저것
wn42
전체 방문자
오늘
어제
  • 분류 전체보기 (113)
    • 프로그래머스 (23)
      • LV1 (11)
      • LV2 (1)
      • LV3 (3)
      • 연습 (8)
    • 딥러닝 공부 (0)
      • 머신러닝&딥러닝 이론 (0)
    • 임베디드 (17)
      • Adventure Design (1)
      • 센서기반모바일로봇 (5)
      • ROS (9)
      • Google Coral (2)
    • C++ (38)
      • C++ 기초 (34)
      • 자료구조 및 알고리즘 (4)
    • Python (14)
      • 기본 파이썬 문법 (6)
      • Python 기초 (8)
    • 빅데이터 (9)
      • 빅데이터 첫걸음 시작하기(국비지원) (5)
      • 빅데이터 공부 (4)
    • 알고리즘 공부 (2)
      • 기본 알고리즘 (2)
    • 전자공학 (10)
      • 반도체 공정 (3)
      • 무선데이터통신 (7)
      • 반도체공학 (0)
    • C# (0)
      • C# 기본 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • K디지털크레딧
  • Python
  • 조건문
  • 패스트캠퍼스
  • 데이터분석 인강
  • 프로그래머스
  • numpy
  • 큐
  • c++
  • 데이터분석
  • 클래스
  • 정렬
  • 스택
  • 변수
  • Queue
  • 빅데이터
  • 노드
  • 바이트디그리
  • 빅데이터 첫걸음 시작하기
  • 상속
  • google coral
  • 파이썬
  • 인스턴스
  • stl
  • ROS
  • 반복문
  • 소멸자
  • 딥러닝
  • 내일배움카드
  • 스택/큐

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
wn42

코딩이랑 이것저것

C++/C++ 기초

[C++] 구조체(Struct)

2022. 12. 28. 14:49

 구조체 

C/C++에서 구조화된 데이터를 처리할 때 사용

  • 하나 이상의 변수(같거나 다른 데이터형을 갖는)를 묶어 새로운 자료형을 정의하는 도구
  • 원시 자료형(int, float 등) 외에 새로운 데이터 타입을 직접 정의할 수 있게 함
  • C++에서는 기본 접근 제어자가 다른 것 말고는 클래스와 동일하다. (구조체-public, 클래스-private)

 

구조체 생성

구조체 정의

  • 구조체는 다음과 같이 정의한다.
  • 구조체를 정의할 때는 멤버 변수를 초기화할 수 없다.
// 정의 방법 1: 구조체만 정의
struct 구조체이름 {
    /*멤버들*/
    int age;
    string name;
};     // 마지막에 무조건 ;(세미콜론) 붙여야 함
// 정의 방법 2: 구조체와 함께 구조체 변수를 정의
struct 구조체이름 {
    /*멤버들*/
    int age;
    string name;
} 구조체변수이름;

 

구조체 변수 정의 및 멤버 변수 접근

  • 다음과 같이 구조체 변수를 정의한다.
struct 구조체명 구조체변수명
  • '.' (온점)을 이용하여 멤버 변수에 접근한다.
// 구조체명.구조체변수명
human.age = 19;

 

Ex) 간단한 구조체 예제

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

struct human {
	int age;
	double height;
	string name;
};

int main() {
	// 1. 구조체 변수 정의 후 멤버 변수 초기화
	struct human person1;
	person1.age = 19;
	person1.height = 182.3;
	person1.name = "minwoo";

	// 2. 구조체 변수 정의와 동시에 멤버 변수 초기화
	struct human person2 = {20, 178.5, "hyunsoo"};

	cout << person1.name << ", " << person1.age << ", " << person1.height << endl;
	cout << person2.name << ", " << person2.age << ", " << person2.height << endl;
	
	return 0;
}
minwoo, 19, 182.3
hyunsoo, 20, 178.5
  • 하나의 새로운 구조체를 정의하여 서로 다른 데이터형을 담아 정리할 수 있다.
  • 구조체 변수를 정의한 후 멤버 변수를 초기화하는 방식과, 정의와 동시에 초기화하는 방식이 있다.

 

Ex) 구조체 정의와 동시에 구조체 변수 정의 및 멤버 초기화

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

struct human {
	int age;
	double height;
	string name;
} person = { 23, 162.5, "Minji" };	// 구조체 변수 정의 및 멤버 초기화

int main() {
	cout << person.name << ", " << person.age << ", " << person.height << endl;
	return 0;
}
Minji, 23, 162.5
  • 구조체를 정의하면서 동시에 구조체 변수를 정의하고, 구조체 변수의 멤버를 초기화할 수 있다.

 

구조체 배열 만들기

  • 구조체에도 배열 자료구조를 사용할 수 있다.
#include <iostream>
#include <string>
using namespace std;

struct human {
	int age;
	double height;
	string name;
};

int main() {
	struct human person[2] = {
		{19, 182.3, "minwoo"},
		{20, 178.5, "hyunsoo"}
	};

	cout << person[0].name << ", " << person[0].age << ", " << person[0].height << endl;
	cout << person[1].name << ", " << person[1].age << ", " << person[1].height << endl;
	
	return 0;
}
minwoo, 19, 182.3
hyunsoo, 20, 178.5

 

구조체와 메모리

  • 구조체가 메모리에 어떤 식으로 저장되는지 확인한다.

 

Ex) 구조체 메모리 확인하기

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

struct human {
	int age;
	double height;
	string name;
};

int main() {
	struct human person[2] = {
		{19, 182.3, "minwoo"},
		{20, 178.5, "hyunsoo"}
	};
	cout << "person[0]의 크기: " << sizeof(person[0]) << endl;
	cout << "person[0]의 시작주소: " << &person[0] << endl;
	cout << "person[0].age의 시작주소: " << &person[0].age << endl;
	cout << "person[0].height의 시작주소: " << &person[0].height << endl;
	cout << "person[0].name의 시작주소: " << &person[0].name << endl << endl;

	cout << "person[1]의 크기: " << sizeof(person[1]) << endl;
	cout << "person[1]의 시작주소: " << &person[1] << endl;
	cout << "person[1].age의 시작주소: " << &person[1].age << endl;
	cout << "person[1].height의 시작주소: " << &person[1].height << endl;
	cout << "person[1].name의 시작주소: " << &person[1].name << endl;
	return 0;
}
person[0]의 크기: 48
person[0]의 시작주소: 006FFB58
person[0].age의 시작주소: 006FFB58
person[0].height의 시작주소: 006FFB60
person[0].name의 시작주소: 006FFB68

person[1]의 크기: 48
person[1]의 시작주소: 006FFB88
person[1].age의 시작주소: 006FFB88
person[1].height의 시작주소: 006FFB90
person[1].name의 시작주소: 006FFB98
  • 4bytes(int) + 8bytes(double) + 28bytes(string) = 40bytes가 되어야 한다고 생각했지만, 총 크기는 48bytes이다.
  • 사실, 각 멤버 변수는 메모리 상에 1word 단위로 저장된다. ▶ 1word(32bit) - 4bytes, 1word(64bit) - 8bytes
  • 필자의 컴퓨터는 64bit이므로 1word가 8bytes 단위이다.
  • int와 double은 모두 1word 단위로 저장되어 8bytes만큼의 메모리를 할당받았다.
  • string은 기본 메모리 크기가 28bytes이지만, 1word의 배수가 되어야 하기에 32bytes만큼 할당되었다.
  • 따라서 구조체는 8bytes(int) + 8bytes(double) + 32bytes(string) = 48bytes의 메모리 크기를 갖는다.
  • 멤버변수끼리, 구조체끼리는 서로 순차적으로 연결되어 메모리 공간에 저장됨을 주소를 통해 알 수 있다.
  • 내용이 틀릴 수도 있으니 잘못된 부분은 댓글 부탁드립니다.

이에 관련된 자세한 내용은 아래 블로그에서 잘 정리하셨으니 참고바랍니다.

 

C언어 구조체의 메모리 사이즈(크기 계산)

* 구조체를 배우고 얼마 안되서, 구조체를 선언하여 그 크기를 계산해본적이 있다. 그런데...내가 상상했던...

blog.naver.com

 

 

구조체 포인터

  • 말 그대로 구조체를 가리키는 포인터를 말한다.
  • 구조체도 하나의 타입(형)이며, 메모리 공간에 저장되기 때문에 포인터로 시작주소를 가리킬 수 있다.

 

구조체 포인터 생성

#include <iostream>
using namespace std;

struct dummy {
	int a, b;
};

int main() {
	struct dummy d;
	struct dummy *ptr;    // 구조체 포인터 정의

	ptr = &d;    // 구조체 포인터이기 때문에 & 연산자 이용

	(*ptr).a = 1;
	(*ptr).b = 2;

	cout << "d의 멤버 a: " << d.a << endl;
	cout << "d의 멤버 b: " << d.b << endl;
	cout << "ptr의 size: " << sizeof(ptr) << endl;

	return 0;
}
d의 멤버 a: 1
d의 멤버 b: 2
ptr의 size: 4
  • 여기서 ptr은 struct dummy라는 형을 가리키는 포인터이다.
  • 구조체 포인터 ptr도 다른 모든 포인터와 동일하게 4byte의 메모리 공간을 차지한다.
  • 구조체는 변수이지, 배열이 아니다. ▶따라서 구조체 포인터를 만들 때, 구조체 변수의 이름이 아닌 & 연산자 + 구조체 변수 이름을 사용하여 구조체가 정의된 메모리 주소를 가져온다.

 

-> 연산자

  • 포인터의 경우 () 소괄호의 역할이 매우 크다. 다음 예시를 보자.

 

Ex) 포인터에 ( ) 소괄호를 붙이지 않는 경우

#include <iostream>
using namespace std;

struct dummy {
	int a, b;
};

int main() {
	struct dummy d;
	struct dummy *ptr;

	ptr = &d;

	*ptr.a = 1;    // 소괄호를 붙이지 않는 경우
	(*ptr).b = 2;

	cout << "d의 멤버 a: " << d.a << endl;
	cout << "d의 멤버 b: " << d.b << endl;

	return 0;
}

  • 소괄호를 넣지 않아서 오류가 발생하였다.
  • 이는 *(asterisk)과 .(period)연산자의 우선 순위에 의해 발생한 오류이다. 아래의 우선순위 표를 보자.

  • .(period)가 *(asterisk)보다 우선순위가 빠르다.
  • 따라서 *ptr.a는 *(ptr.a)의 순서로 처리된다. ▶ 여기서 ptr은 포인터로, 구조체가 아니기 때문에 멤버 변수 a를 가지고 있지 않다. 그러므로 a 멤버에 접근 시 오류가 발생한다.
  • 그렇기 때문에 구조체 포인터에서 ( ) 소괄호를 빠뜨리지 않고 꼭 적어야 한다.
  • 그런데 ( ) 소괄호를 계속 적는 것은 매우 귀찮은 일이다. ▶ 프로그래머들은 이 문제를 해결하기 위해 새로운 연산자를 개발했다. 바로 -> 연산자이다.

 

Ex) -> 연산자 이용하여 포인터로 멤버 변수에 접근하기

#include <iostream>
using namespace std;

struct dummy {
	int a, b;
};

int main() {
	struct dummy d;
	struct dummy *ptr;

	ptr = &d;

	ptr->a = 1;      // -> 연산자 이용하기
	(*ptr).b = 2;    // () 소괄호를 이용하는 기존 방식

	cout << "d의 멤버 a: " << d.a << endl;
	cout << "d의 멤버 b: " << d.b << endl;

	return 0;
}
d의 멤버 a: 1
d의 멤버 b: 2
  • ptr->a = 1 이라는 표현은 (*ptr).a = 1 과 정확히 일치하는 문장이다.
  • 단순히 편의를 위해 -> 연산자가 도입되었으며, 이를 사용하면 귀찮게 ( ) 소괄호를 적을 필요가 없다.

 

구조체 포인터 연습

Ex) 포인터 멤버 연습 1

#include <iostream>
using namespace std;

struct dummy {
	int* pointer;
};

int main() {
	struct dummy d;
	struct dummy* ptr;    // 구조체 포인터 정의

	int i = 0;

	ptr = &d;

	d.pointer = &i;    // pointer 멤버가 포인터이므로, i의 주소값을 넘김. (*ptr).pointer = &i; 와 동일
	*d.pointer = 3;    // d의 멤버 pointer가 가리키는 변수 값을 3으로 변경. *(d.pointer) = 3; 와 동일
	cout << "i의 값: " << i << endl;

	*ptr->pointer = 4;    // -> 연산자 이용하여 pointer 멤버가 가리키는 변수 값을 4로 변경
	cout << "i의 값: " << i << endl;

	return 0;
}
i의 값: 3
i의 값: 4
  • 멤버 변수에는 포인터를 포함시킬 수 있다.

 

Ex) 포인터 멤버 연습2 (Call-by-reference)

#include <iostream>
using namespace std;

int plus_ten(int*);

struct dummy {
	int i;
};

int main() {
	struct dummy d;
	struct dummy* ptr;    // 구조체 포인터 정의

	ptr = &d;
	ptr->i = 0;    // 멤버 i 초기화

	cout << "i의 값(함수 호출 전): " << ptr->i << endl;

	plus_ten(&d.i);     // 값 변경을 위해 구조체 멤버의 주소를 전달. &(d.i)와 동일
	cout << "i의 값(함수 호출 후1): " << d.i << endl;

	plus_ten(&ptr->i);    // 값 변경을 위해 -> 연산자 이용. &(ptr->i)와 동일
	cout << "i의 값(함수 호출 후2): " << ptr->i << endl;

	return 0;
}

int plus_ten(int* num) {
	*num += 10;
	return 0;
}
i의 값(함수 호출 전): 0
i의 값(함수 호출 후1): 10
i의 값(함수 호출 후2): 20
  • 구조체 포인터를 이용하여 call-by-reference를 구현한 예제이다.
  • 연산자의 우선순위를 잘 따져야 한다. ▶ &d.i → &(d.i)

 

 

구조체 복사하기

  • 구조체는 대입 연산을 통해 간단히 복사가 가능하다.
#include <iostream>
using namespace std;

struct dummy {
	int i;
	string str;
};

int main() {
	struct dummy a, b;

	a.i = 10;
	a.str = "Hello!";

	b = a;    // 대입하여 복사하기
	
	cout << "a의 멤버 i: " << a.i << endl;
	cout << "a의 멤버 str: " << a.str << endl;
	cout << "b의 멤버 i: " << b.i << endl;
	cout << "b의 멤버 str: " << b.str << endl;

	return 0;
}
a의 멤버 i: 10
a의 멤버 str: Hello!
b의 멤버 i: 10
b의 멤버 str: Hello!

 

 

구조체 매개변수

  • 구조체를 인자(매개변수)로 전달할 수 있다.
  • 구조체를 인자로 전달할 때 call-by-reference가 이루어져야 함을 유의한다.

 

구조체 인자 전달하기

#include <iostream>
using namespace std;

int set_struct(struct dummy*, int, string);

struct dummy {
	int i;
	string str;
};

int main() {
	struct dummy a;

	set_struct(&a, 10, "Hello!!");

	cout << "a의 멤버 i: " << a.i << endl;
	cout << "a의 멤버 str: " << a.str << endl;

	return 0;
}

int set_struct(struct dummy *sr, int i, string str) {
	sr->i = i;
	sr->str = str;

	return 0;
}
a의 멤버 i: 10
a의 멤버 str: Hello!!
  • 구조체 변수 a의 주소를 인자로 전달함으로써 구조체 멤버에 접근하여 값을 초기화할 수 있다.
  • 이때 함수 set_struct의 매개변수 sr은 구조체 포인터이며, 절대 구조체가 아니다. ▶ 단지 sr은 구조체 변수 a가 저장된 메모리의 시작주소를 담는다.

 

 

구조체 안의 구조체

  • 구조체는 구조체를 멤버로 가질 수 있다.
#include <iostream>
using namespace std;

struct profiles {
	int gradeNum;
	int classNum;
	int studentNum;
	string name;
};

struct school {
	struct profiles data;
	string schoolName;
};


int main() {
	struct school High;

	High.schoolName = "Seoul";
	High.data.gradeNum = 2;
	High.data.classNum = 7;
	High.data.studentNum = 17;
	High.data.name = "minji";

	cout << "학교이름: " << High.schoolName << " High School" << endl;
	cout << "학년: " << High.data.gradeNum << endl;
	cout << "반: " << High.data.classNum << endl;
	cout << "이름: " << High.data.name << endl;
	cout << "번호: " << High.data.studentNum << endl;

	return 0;
}
학교이름: Seoul High School
학년: 2
반: 7
이름: minji
번호: 17
  • 구조체 또한 int, float와 같은 하나의 형(타입)이기 때문에 구조체가 구조체를 멤버로 가질 수 있는 것은 당연하다.
  • 연산자 우선순위에 의해 High.data.name은 (High.data).name로 처리된다. ▶ High의 멤버 data의 멤버 name

 

 

구조체 반환하기

  • 구조체는 하나의 타입(형)이기 때문에 반환(return)이 가능하다.
  • 아래는 구조체를 반환하는 함수를 구현한 예제이다.
#include <iostream>
using namespace std;

struct dummy func(int);

struct dummy {
	int a;
};

int main() {
	struct dummy d;

	d = func(10);    // return한 구조체를 복사

	cout << "d의 멤버 a: " << d.a << endl;
	return 0;
}

struct dummy func(int i) {
	struct dummy A;
	A.a = i;

	return A;
}
d의 멤버 a: 10

 

 

구조체 안에 함수 넣기

  • 구조체 안에 함수를 넣고, 함수에 구조체 멤버 변수들을 활용할 수 있다.
  • 구조체 안의 들어있는 함수 또한 멤버이며, 이를 멤버 함수 또는 멤버 메서드라고 한다.
#include <iostream>
using namespace std;

struct dummy {
	int a, b;
	
	// 멤버 함수(멤버 메서드)
	int mul() {
		return a * b;    // 멤버 변수 사용 가능
	}
};

int main() {
	struct dummy d = {5, 10};

	cout << d.a << " x " << d.b << " = " << d.mul() << endl;
	return 0;
}
5 x 10 = 50

 

    'C++/C++ 기초' 카테고리의 다른 글
    • [C++] 열거형(Enum)
    • [C++] 공용체(Union)
    • [C++] typedef
    • [C++] 디버깅(Debugging)
    wn42
    wn42
    코딩이랑 이것저것 하는 블로그

    티스토리툴바