지금까지는 정수, 실수, 문자, 배열처럼 한 가지 종류의 데이터만 다뤄왔습니다. 하지만 실제 프로그램을 짜다 보면 “이름, 나이, 점수”처럼 서로 다른 자료형의 데이터를 하나의 묶음으로 관리하고 싶을 때가 많습니다. 이럴 때 사용하는 것이 바로 구조체(struct)입니다. 오늘은 구조체와, 구조체와 비슷하게 생겼지만 동작 방식이 완전히 다른 공용체(union)에 대해 알아보겠습니다.

구조체란 무엇인가

구조체는 서로 다른 자료형의 변수들을 하나의 이름으로 묶어서 관리할 수 있게 해주는 사용자 정의 자료형입니다. 예를 들어 학생 한 명의 정보를 표현하려면 이름(문자열), 학번(정수), 평균 점수(실수)가 필요한데, 이 세 가지를 각각 따로 관리하기보다는 Student라는 하나의 구조체로 묶으면 훨씬 관리하기 편해집니다.

#include <stdio.h>
#include <string.h>

struct Student {
    char name[20];
    int id;
    double average;
};

int main(void) {
    struct Student s1;

    strcpy(s1.name, "홍길동");
    s1.id = 20260924;
    s1.average = 92.5;

    printf("이름: %s\n", s1.name);
    printf("학번: %d\n", s1.id);
    printf("평균 점수: %.1f\n", s1.average);

    return 0;
}

이 코드를 컴파일하고 실행하면 다음과 같은 결과가 출력됩니다.

이름: 홍길동
학번: 20260924
평균 점수: 92.5

struct Student라는 새로운 자료형을 정의한 뒤, struct Student s1;으로 변수를 선언했습니다. 구조체의 각 멤버에는 점(.) 연산자를 이용해서 접근합니다. 문자열은 배열이기 때문에 대입 연산자 =를 바로 쓸 수 없어서 strcpy 함수로 복사해야 한다는 점도 함께 기억해두면 좋습니다.

typedef로 구조체 이름 간단하게 쓰기

매번 struct Student라고 쓰는 것이 번거롭다면, typedef를 이용해서 별칭을 만들 수 있습니다. 이렇게 하면 변수를 선언할 때 struct 키워드를 생략할 수 있어 코드가 훨씬 깔끔해집니다.

#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

Point makeMidpoint(Point a, Point b) {
    Point mid;
    mid.x = (a.x + b.x) / 2;
    mid.y = (a.y + b.y) / 2;
    return mid;
}

int main(void) {
    Point p1 = {0, 0};
    Point p2 = {10, 20};

    Point mid = makeMidpoint(p1, p2);

    printf("p1 = (%d, %d)\n", p1.x, p1.y);
    printf("p2 = (%d, %d)\n", p2.x, p2.y);
    printf("중점 = (%d, %d)\n", mid.x, mid.y);

    return 0;
}

실행 결과는 다음과 같습니다.

p1 = (0, 0)
p2 = (10, 20)
중점 = (5, 10)

이 예제에서 주목할 부분은 makeMidpoint 함수가 구조체를 값으로 반환한다는 점입니다. 구조체는 배열과 달리 함수의 매개변수로 넘기거나 반환값으로 사용할 때 전체 내용이 그대로 복사됩니다. 그래서 p1, p2 원본은 전혀 바뀌지 않고, 계산된 결과만 새로운 구조체 mid에 담기게 됩니다.

공용체는 구조체와 무엇이 다른가

공용체(union)는 겉모습이 구조체와 비슷하지만 메모리를 사용하는 방식이 완전히 다릅니다. 구조체는 멤버마다 각자의 메모리 공간을 따로 가지지만, 공용체는 모든 멤버가 같은 메모리 공간을 공유합니다. 즉 공용체의 크기는 멤버 중 가장 큰 자료형의 크기와 같고, 한 번에 하나의 멤버 값만 유효합니다.

#include <stdio.h>

union Data {
    int i;
    float f;
    char str[20];
};

int main(void) {
    union Data data;

    printf("union Data의 크기: %zu 바이트\n", sizeof(data));

    data.i = 10;
    printf("정수로 사용: %d\n", data.i);

    data.f = 3.14f;
    printf("실수로 사용: %.2f\n", data.f);

    return 0;
}

실행 결과는 대략 다음과 같이 나옵니다.

union Data의 크기: 20 바이트
정수로 사용: 10
실수로 사용: 3.14

data.i에 값을 넣은 뒤 data.f에 다른 값을 넣으면, 같은 메모리 공간을 덮어쓰기 때문에 data.i에 넣었던 값은 더 이상 의미가 없어집니다. 공용체는 이렇게 여러 자료형 중 딱 하나만 골라서 저장하고 싶을 때, 그리고 메모리를 절약하고 싶을 때 사용합니다. 다만 구조체보다 사용 빈도는 훨씬 낮고, 어떤 멤버가 지금 유효한지를 프로그래머가 직접 관리해야 한다는 점에서 실수하기 쉬우니 주의가 필요합니다.

코드 동작 설명 및 예상 실행 결과

  • 첫 번째 예제는 struct Student 구조체에 이름, 학번, 평균 점수를 담아 출력하며, 문자열 멤버는 strcpy로 복사해야 한다는 점을 보여줍니다.
  • 두 번째 예제는 typedef로 만든 Point 구조체를 함수 인자와 반환값으로 사용하는데, 구조체는 배열과 달리 전달 시 값 자체가 복사되므로 원본 p1, p2는 변경되지 않습니다.
  • 세 번째 예제는 union Data의 크기가 멤버 중 가장 큰 str[20](20바이트)와 같다는 것과, 멤버 값을 새로 넣으면 이전 멤버의 값이 덮어써진다는 것을 보여줍니다.
  • 세 예제 모두 gcc 파일명.c -o 실행파일로 컴파일 후 실행하면 위와 동일한 결과를 확인할 수 있습니다.

핵심 요약

  • 구조체는 서로 다른 자료형의 데이터를 하나의 이름으로 묶어서 관리할 수 있게 해주는 사용자 정의 자료형입니다.
  • 구조체 멤버에는 점(.) 연산자로 접근하며, 문자열 멤버는 strcpy로 값을 복사해야 합니다.
  • typedef를 사용하면 struct 키워드 없이 구조체 이름을 사용할 수 있어 코드가 간결해집니다.
  • 구조체는 함수에 전달되거나 반환될 때 전체 값이 복사되지만, 공용체는 모든 멤버가 같은 메모리 공간을 공유해서 한 번에 하나의 값만 유효합니다.

다음 편에서는 프로그램 실행 중에 필요한 만큼 메모리를 직접 할당하고 해제하는 방법인 “동적 메모리 할당 (malloc/free)”을 주제로 다뤄보겠습니다.