C언어 구조체 (union, enum, 실기)
지금까지 살펴본 변수와 배열은 모두 단일 자료형의 데이터를 다루는 도구였다. 그러나 현실의 데이터는 그렇게 단순하지 않다. 학생 한 명을 표현하려면 학번(정수), 이름(문자열), 학점(실수) 같은 서로 다른 자료형의 정보를 한 묶음으로 관리해야 하며, 이를 가능하게 하는 도구가 바로 구조체(struct)이다(출처: cppreference — struct/union/enum). 이와 비슷하면서도 결정적으로 다른 형태인 공용체(union)와 열거형(enum)도 함께 다뤄지며, 세 가지 모두 사용자가 정의하는 새로운 자료형으로 묶인다. 정보처리기사 실기에서도 구조체 멤버 접근과 typedef를 활용한 코드의 출력 결과를 묻는 문제가 자주 출제된다. 본 글은 코드 예시와 함께 세 자료형의 차이를 한 번에 정리한다. 제가 학교 C언어 프로젝트에서 가장 자주 만난 사고가 정확히 구조체 포인터의 (*ps).id와 ps->id를 혼동한 일이었고, 한 줄짜리 비교를 외운 후로는 시험 답안에서 자동 작성이 가능해졌다.

구조체의 선언과 멤버 접근
구조체(Structure)란 서로 다른 자료형의 변수들을 하나의 단위로 묶어 관리하는 사용자 정의 자료형이다. 같은 자료형만 묶을 수 있는 배열과 달리 구조체는 정수·실수·문자·배열·다른 구조체까지 자유롭게 묶을 수 있어 현실의 복합적인 데이터를 표현하기에 매우 적합하다. C언어에서 구조체를 정의할 때는 struct 키워드를 사용하며, 한 번 정의된 구조체는 일반 자료형처럼 사용할 수 있다.
struct Student {
int id;
char name[20];
double gpa;
};
struct Student s1 = {1, "KIM", 4.5};
printf("%d\n", s1.id); // 1
printf("%s\n", s1.name); // KIM
매번 struct Student라고 길게 적는 것이 번거로우니, typedef 키워드를 활용해 별칭을 부여하는 방식이 표준 패턴으로 자리 잡았다. 여기서 typedef란 기존 자료형에 새로운 이름을 부여해 코드의 가독성과 일관성을 높이는 키워드를 가리킨다.
typedef struct {
int id;
char name[20];
double gpa;
} Student;
Student s2 = {2, "LEE", 4.0};
printf("%s\n", s2.name); // LEE
구조체 포인터를 사용할 때는 점(.) 연산자 대신 화살표(->) 연산자를 사용한다. ps->id는 (*ps).id와 동일한 의미이며, 두 표현 모두 같은 결과를 만든다는 사실은 정보처리기사 실기에서 매회 출제되는 핵심 포인트이다.
Student *ps = &s2;
printf("%d\n", ps->id); // 2
printf("%d\n", (*ps).id); // 2 (동일)
구조체는 메모리에 저장될 때 정렬(Alignment) 규칙에 따라 멤버 사이에 패딩(Padding) 바이트가 삽입되기 때문에, 멤버 크기의 단순 합과 sizeof 결과가 다를 수 있다는 점을 기억해야 한다. 일반적으로 가장 큰 멤버의 크기에 맞춰 정렬되며, 이 규칙은 CPU의 효율적인 메모리 접근을 보장하기 위한 것이다.
공용체와 열거형
공용체(Union)는 구조체와 거의 똑같이 보이지만, 결정적인 차이가 하나 있다. 모든 멤버가 같은 메모리 공간을 공유한다는 점이다. 즉 구조체는 모든 멤버를 위한 공간을 따로 할당해 각 멤버가 독립적인 값을 가질 수 있지만, 공용체는 가장 큰 멤버의 크기만큼만 메모리를 할당하고 모든 멤버가 그 공간을 같이 쓴다. 따라서 한 멤버에 값을 쓰면 다른 멤버의 값이 덮어써진다.
| 비교 항목 | 구조체(struct) | 공용체(union) |
|---|---|---|
| 메모리 할당 | 모든 멤버 합 (+ 패딩) | 가장 큰 멤버 크기 |
| 멤버 값 | 각 멤버 독립 | 동시에 한 멤버만 의미 있음 |
| 대표 사용처 | 일반 데이터 모델링 | 메모리 절약·비트 패턴 해석 |
union Data {
int i;
float f;
char str[20];
};
union Data d;
d.i = 100;
printf("%d\n", d.i); // 100
d.f = 3.14; // i가 차지하던 공간을 f가 덮어씀
printf("%d\n", d.i); // 쓰레기 값 (3.14의 비트 표현을 정수로 해석)
printf("%lu\n", sizeof(d)); // 20 (가장 큰 멤버 str)
공용체는 메모리를 절약해야 하는 임베디드 환경이나, 같은 데이터를 여러 형식으로 해석해야 하는 경우(예: float의 비트 패턴을 정수로 검사)에 유용하다. 솔직히 제 경험상 학교 일반 응용 프로그래밍에서는 공용체를 거의 쓸 일이 없었지만, 임베디드 동아리에서 4바이트 센서 값을 두 개의 16비트로 분리해 해석할 때 처음 union의 가치를 손끝으로 받아들였다.
열거형(Enumeration)은 정수 상수에 의미 있는 이름을 부여해 가독성을 높이는 자료형이다. enum 키워드를 사용해 정의하며, 별도로 값을 명시하지 않으면 첫 멤버는 0부터 시작해 1씩 증가하는 정수가 자동으로 부여된다.
enum Day {
MON, // 0
TUE, // 1
WED, // 2
FRI = 10, // 10
SAT, // 11
SUN // 12
};
enum Day today = WED;
printf("%d\n", today); // 2
printf("%d\n", SAT); // 11
열거형의 가장 큰 가치는 매직 넘버(Magic Number) 제거이다. 여기서 매직 넘버란 코드 안에 그 의미가 설명 없이 떠 있는 임의의 숫자 상수를 가리키며, 시간이 지나면 그 의미를 아무도 기억하지 못하게 되는 유지보수성의 주범이다. 코드에서 0, 1, 2 같은 의미 없는 숫자 대신 MON, TUE, WED 같은 이름을 사용하면 읽는 사람이 그 값의 의도를 즉시 파악할 수 있다.
정보처리기사 실기 빈출 구조체·공용체 예제
정보처리기사 실기에서 구조체와 공용체는 매회 출제되며, 출제 패턴은 다음 네 가지로 정리된다(출처: Q-Net 정보처리기사).
유형 1 — 구조체 멤버 접근과 typedef.
typedef struct {
int x;
int y;
} Point;
int main(void) {
Point p1 = {3, 4};
Point p2;
p2.x = p1.x * 2;
p2.y = p1.y * 2;
printf("%d %d\n", p2.x, p2.y); // 6 8
return 0;
}
유형 2 — 구조체 포인터와 화살표 연산자.
typedef struct {
int num;
char *name;
} Item;
int main(void) {
Item it = {10, "apple"};
Item *p = ⁢
printf("%d\n", p->num); // 10
printf("%s\n", p->name); // apple
printf("%c\n", *(p->name)); // a (문자열 첫 글자)
return 0;
}
*(p->name)이 'a'를 반환하는 이유는 p->name이 "apple"의 첫 글자 주소이기 때문이며, 이 주소를 역참조하면 첫 글자 'a'가 나오는 것이다.
유형 3 — 구조체 배열.
typedef struct {
int id;
int score;
} Student;
int main(void) {
Student arr[3] = { {1,85}, {2,92}, {3,78} };
int sum = 0;
for (int i = 0; i < 3; i++) sum += arr[i].score;
printf("%d\n", sum); // 255
printf("%d\n", arr[1].score); // 92
return 0;
}
유형 4 — 공용체의 메모리 공유.
union Sample {
int a;
char c;
};
int main(void) {
union Sample s;
s.a = 65; // 'A'의 ASCII 값
printf("%d\n", s.a); // 65
printf("%c\n", s.c); // A (같은 메모리를 char로 해석)
s.c = 'B'; // a의 일부 바이트를 'B'(66)로 덮어씀
printf("%d\n", s.a); // 66 (또는 시스템에 따라 다름)
return 0;
}
공용체에서 한 멤버 변경이 다른 멤버에도 영향을 미친다는 핵심 특성을 묻는 패턴이다. 같은 메모리를 다른 자료형으로 해석하면 어떤 값이 나오는지 추적할 수 있어야 한다. 솔직히 이건 예상 밖이었는데, 같은 65라는 비트 패턴이 int로는 65로, char로는 'A'로 해석되는 모습을 처음 실행해 본 후로는 "자료형은 메모리가 아니라 메모리를 해석하는 시선"이라는 한 줄을 손끝으로 받아들였다. 결국 구조체는 데이터의 묶음이고, 공용체는 메모리의 공유이며, 열거형은 의미의 명명이라는 세 단어로 요약할 수 있다. 이 세 가지 도구를 적절히 조합해 사용할 줄 알면 코드의 표현력과 가독성이 비약적으로 향상되며, 자료구조와 운영체제의 후속 학습으로 자연스럽게 연결된다.
메타 디스크립션: C언어의 사용자 정의 자료형인 구조체(struct), 공용체(union), 열거형(enum)의 차이와 메모리 동작, typedef를 활용한 별칭 선언, 그리고 정보처리기사 실기 빈출 4패턴을 코드 예시와 함께 정리합니다.