Notice
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Today
Total
관리 메뉴

Study Note

[C/C++ Syntax] #014 구조체(Structure) 본문

- 프로그래밍 언어(Programming Language)/C\C++

[C/C++ Syntax] #014 구조체(Structure)

mymir 2021. 1. 26. 18:48

이전 글에서 같은 자료형의 데이터들을 일괄적으로 처리할 수 있는 자료구조인 배열에 대해 배웠습니다. 하지만 코딩을 하다보면 때론 다른 자료형의 데이터들을 묶어 처리하면 더욱 편리하게 사용할 수 있습니다. 이번 글에선 이를 위한 자료구조인 구조체(structure)에 대해 다뤄보도록 하겠습니다.

 

 

 

구조체(Structure)


구조체는 새롭게 만들 때 마다 그 묶음에 포함되는 자료가 다르기 때문에 함수와 같이 정의를 하는 단계가 필요합니다. 정의 방법은 다음과 같습니다.

먼저, struct는 고정적으로 작성해주어야 합니다. 이어서 태그는 구조체의 이름으로, 자료의 묶음을 식별화 하기 위해 필요합니다. 그리고 중괄호로 자료를 작성합니다. 구조체에 포함되는 자료를 각각 멤버라고 말하며 변수의 선언과 같이 자료형을 적고 잇따라 멤버 이름을 적습니다.

그리고 구조체의 정의 일종의 문장에 해당되어, 중괄호를 닫은 후 반드시 세미콜론을 붙여주어야 합니다.

 

이렇게 정의를 하면 곧바로 사용하는 것이 아니라 변수와 같이 작성한 구조체를 선언하여 사용합니다. 많이 사용되는 비유로 정의 단계를 붕어빵 틀에 비유하면 선언 단계는 실제로 붕어빵을 찍어내는 것과 같은 맥락입니다.

 

구조체 변수의 선언은 다음과 같습니다.

기본형 변수와 비교하자면 struc와 태그를 합쳐서 하나의 자료형 꼴로 보면 되는 것입니다. 다만 C++에서는 struct를 생략하여 써도 무방합니다.

더보기

C++에서는 구조체와 이후에 배울 클래스를 크게 구별하지 않습니다. 구조체 내부에 멤버 함수도 선언할 수 있구요. 디폴트 접근값 정도의 차이만 있습니다.

 

그리고 구조체도 초기화를 할 수 있는데, 그 방법은 다음과 같습니다.

배열의 초기화와 같이 중괄호를 이용해 구조체를 초기화 합니다. 값은 구조체 멤버의 개수와 같거나 작게 적어주며 구조체를 정의할 때 적어준 멤버의 순서와 일치하게 대입됩니다. 이때, 각 멤버의 자료형과 대입할 값의 자료형이 같도록 유의하여야 합니다.

 

이처럼 구조체 변수를 선언하고 나면 구조체를 사용하기 위해선 각 멤버에 접근할 수 있도록 하는 직접 멤버 참조 연산자 . (온점)를 사용합니다. 다음 예시를 살펴보겠습니다.

#include <iostream>

using namespace std;

struct Physical_data {
	int age;
	double height;
	double weight;
};

int main() {
	struct Physical_data person1 = { 18, 179.9, 65.3};

	cout << "age : " << person1.age << endl;
	cout << "height : " << person1.height << endl;
	cout << "weight : " << person1.weight << endl;
}

 

구조체와 포인터


구조체 변수도 주소 연산자 &를 사용하여 주소를 나타낼 수 있으며 따라서 구조체를 가리키는 포인터도 존재합니다. 다음 예시를 살펴보겠습니다.

#include <iostream>

using namespace std;

struct Physical_data {
	int age;
	double height;
	double weight;
};

int main() {
	struct Physical_data person1 = { 18, 179.9, 65.3};
	struct Physical_data* p = &person1;

	cout << "age : " << (*p).age << endl;
	cout << "height : " << p->height << endl;
	cout << "weight : " << p->weight << endl;
}

먼저 첫 번째 출력을 살펴보면, p가 가리키는 구조체의 age에 접근하기 위해 간접 참조 연산을 먼저 수행하고 직접 멤버 참조 연산을 수행합니다.

현재 구조체는 그다지 복잡하지 않지만, 포인터와 구조체가 중첩되는 관계에서 두 연산자(*와 .)를 매번 구분해가며 사용하는 것은 코드의 가독성도 떨어트리며 사용의 실수도 잦아질 수 있습니다. 때문에 첫 번째 출력과 동일한 출력인 두 번째와 세 번째 출력과 같이 간접 멤버 참조 연산자 ->를 사용합니다.

 

 

 

구조체와 함수


구조체는 배열과 달리 구조체 변수끼리 대입 연산자를 통해 내부 멤버를 한 번에 복사할 수 있습니다. 즉, 일반 변수와 같이 함수의 매개변수로 인자를 넘길 수 있다는 말이지요.

 

하지만 이로인해 빚어지는 오해가 있습니다. 구조체 변수에 바로 비교 연산자를 사용하는 경우입니다. 구조체 변수의 비교는 반드시 각 멤버간 이루어져야 합니다.

다음 예제를 살펴보겠습니다.

#include <iostream>

using namespace std;

void isEqual(struct Physical_data* pd1, struct Physical_data* pd2);

struct Physical_data {
	int age;
	double height;
	double weight;
};

int main() {
	struct Physical_data person1 = { 18, 179.9, 65.3};
	struct Physical_data person2 = person1;
	struct Physical_data person3 = { 18, 180.3, 72.1 };

	isEqual(&person1, &person2);
	isEqual(&person1, &person3);
}

void isEqual(struct Physical_data* pd1, struct Physical_data* pd2) {
	if ((pd1->age == pd2->age) && (pd1->height == pd2->height) && (pd1->weight == pd2->weight))
		cout << "equal" << endl;
	else
		cout << "not equal" << endl;
}

예제에선 함수에 구조체를 전달할 때, 굳이 포인터를 사용해 전달한 것을 볼 수 있습니다. 포인터를 사용하여 크기가 큰 데이터를 가벼운 단위로 전달하는 장점을 살린 방법으로, 구조체에서도 많이 사용되는 전달 방식입니다.

 

 

 

typedef


typedef 예약어는 자료형의 이름을 바꾸어 사용할 수 있도록 기능을 갖추고 있습니다. 구조체와 같이 사용자가 새로 정의한 자료형의 이름이 길 수도 있고, 해당 코드에 특수한 자료형으로써 읽히게 하기 위해 사용합니다. 사용 방식은 다음과 같습니다.

예를 들어, typedef double* DP; 라 하면 DP가 double형 포인터의 다른 이름으로 정의되어 DP d; 라고 선언하면 d가 double형 데이터의 주소를 담을 수 있는 포인터가 되는 것입니다.

 

typedef는 구조체의 정의와 함께 사용할 수 있으며 사용 방식은 다음과 같습니다.

이렇게 typedef를 사용하여 구조체를 정의하면 new name으로 C에서도 struct를 제외하고 구조체 변수를 선언할 수 있게 되는 것입니다.

Comments