C언어에는 다양한 자료 타입들이 있습니다. 정수형(int), 실수형(float, double), 문자형(char), 그리고 short, long 등등 여기에 부호를 결정하는 자료 타입(unsigned)까지 C언어는 생각보다 많은 자료 타입을 가지고 있습니다. 그런데 여러 자료들을 한 번에 저장하고 싶으면 어떻게 해야할까요? 이때 "구조체"라는 것이 사용됩니다. 지금부터 구조체에 대해서 알아보도록 하겠습니다.
typedef 키워드
typedef 키워드는 Type Define의 줄임말로, "타입을 정의하겠다"는 뜻입니다. 이러한 문법이 필요한 이유는 기존의 자료형 중 길이가 긴 표현들을 짧게 표현할 수 있기 때문입니다. 예를 들어 unsigned long long int 같은 자료형을 typedef 문법을 사용하여 길이가 짧게 표현할 수 있다는 것입니다.
typedef 문법을 사용할 때는, 먼저 typedef 키워드를 사용한 후 기존 자료형을 붙입니다. 그 후 자신이 새롭게 정의한 자료형 이름을 붙이면 됩니다. 아까 예시로 들었던 unsigned long long int 자료형을 바꾸는 작업을 진행해보겠습니다.
// 기존 문법: unsigned long long int
unsigned long long int Example;
// typedef 문법
typedef unsigned long long int Data
// unsigned long long int 자료형을 Data라는 이름으로 정의
Data Example;
구조체 선언
서론에서 살펴보았듯, 구조체를 사용하는 이유는 "여러 데이터를 그룹핑"하기 위함입니다. C언어에서는 데이터를 그룹핑하는 문법이 하나 더 있었습니다. 바로 "배열"입니다. 배열도 여러 데이터를 묶어서 관리하고 인덱스를 통하여 접근했습니다. 그런데 배열은 "같은 형태의 데이터"끼리만 묶을 수 있습니다. 다시 말해, 다른 형태의 데이터는 배열을 통해서 그룹핑할 수 없다는 것입니다. 이것이 배열의 한계입니다.
다른 형태의 데이터를 묶을 수 없다는 배열의 단점을 해결하는 문법이 있는데, 바로 "구조체"입니다. 구조체는 크기나 형식이 서로 다른 데이터를 그룹으로 묶어 사용합니다. 그렇게 서로 다른 데이터를 그룹으로 묶은 후 사용자는 새로운 데이터 형식을 선언하게 되는데 그것이 구조체의 원리입니다. 심지어 구조체 안에는 배열과 포인터 또한 선언이 가능하고, 구조체 안에 구조체를 선언하는 것도 가능합니다. 구조체를 선언하는 방법은 다음과 같습니다.
struct Student
{
char name[10]; // 이름 선언 (10byte)
int age; // 나이 선언 (4byte)
char univ[20]; // 대학 선언 (20byte)
float score; // 학점 선언 (4byte)
};
typedef struct People Person;
- 먼저 struct 키워드를 통해 구조체를 선언함을 알립니다.
- struct 키워드 뒤에 구조체 이름을 적습니다.
- 그리고 중괄호 안에 자신이 구성하고 싶은 변수들을 선언합니다.
- typedef 문법을 통하여 자신이 정의한 구조체 자료형을 쓰기 편한 이름으로 재정의합니다.
이러한 방식으로 구조체가 만들어집니다. 가장 마지막에 typedef 문법을 통해 구조체를 재정의하는 이유는 "typedef 키워드를 통해 구조체를 재정의하지 않으면, 매번 구조체를 사용할 때마다, struct 키워드를 붙여야하는데 그 과정을 생략하고자 하는 것"입니다. 위와 같이 구조체를 구성하는 방법도 있고, 처음부터 typedef 문법을 활용하여 구조체를 선언할 수도 있습니다. 지금부터 typedef 문법을 바로 조합하여 구조체 변수를 선언하는 방법을 살펴보겠습니다.
typedef struct
{
char name[10]; // 이름 선언 (10byte)
int age; // 나이 선언 (4byte)
char univ[20]; // 대학 선언 (20byte)
float score; // 학점 선언 (4byte)
} Person;
구조체를 선언하는 경우, 많은 사람들이 이 방식을 선호합니다.
구조체 요소 사용하기
배열에서는 배열의 요소를 사용할 때, 인덱스를 활용하였습니다. 구조체에서는 구조체의 각 요소를 사용할 때는 .(요소 지정) 연산자와 함께 자신이 사용할 요소의 이름을 적어서 사용하면 됩니다.
typedef struct Student
{
char name[10]; // 이름 선언 (10byte)
int age; // 나이 선언 (4byte)
char univ[20]; // 대학 선언 (20byte)
float score; // 학점 선언 (4byte)
} Person;
int main() {
Person data; // Person 구조체 자료형으로 data를 선언
data.age = 22; // data 변수의 age를 22로 선언
data.score = 4.21; // data 변수의 score를 4.21로 선언
return 0;
}
이렇게 .을 활용하면 구조체의 각 요소에 접근할 수 있습니다. 구조체로 선언한 변수를 포인터로 사용하기 위해서는 .을 사용해서는 안됩니다. 왜냐하면 포인터가 구조체 내부 요소에 접근할 때는, * 연산자를 사용해야 하는데, * 연산자가 . 연산자보다 우선 순위에서 밀립니다. 즉, * 연산자보다 . 연산자가 더 먼저 수행되어서 오류를 내뿜게 됩니다.
그렇기에 연산자 우선순위가 밀리지 않는 -> 연산자를 사용하면 오류 없이 포인터로 구조체 내부 변수를 사용할 수 있게 됩니다.
Person data; // Person 자료형으로 data 변수를 선언합니다.
Person *ptr; // Person 형식으로 선언한 메모리에 접근 가능한 포인터를 선언합니다.
ptr = &data; // 포인터 변수 ptr은 data 변수의 주소값을 저장합니다.
ptr->age = 22; // ptr에 저장된 주소에 가서 age 요소에 22를 대입합니다.