[C 시리즈 10편] struct, enum, typedef로 데이터 구조 설계하기

English version

9편까지 배열과 포인터로 연속된 데이터를 다뤘다면, 이제는 서로 다른 의미의 값을 하나로 묶는 방법이 필요합니다. [[struct|struct]]는 값 묶음을 정의하는 기본 단위이고, [[enum|enum]]은 가능한 상태를 이름으로 정리하는 도구입니다. 여기에 [[typedef|typedef]]를 더하면 긴 선언을 읽기 쉬운 이름으로 바꿀 수 있습니다.

이번 글에서 새로 나오는 용어

  1. 구조체 (Structure): 서로 다른 자료형을 하나의 사용자 정의 자료형으로 묶는 문법
  2. 열거형 (Enum): 정수 상수 목록에 이름을 붙여 의미를 명확히 하는 자료형
  3. 타입 별칭 (Typedef): 기존 선언에 더 짧고 읽기 쉬운 이름을 붙이는 키워드
  4. 도트 연산자 (.): 구조체 변수 안의 필드에 접근하는 연산자

핵심 개념

학습 메모

  • 소요 시간: 60~70분
  • 준비물: 배열, 포인터, 함수 인자 기초
  • 학습 목표: 구조체 정의, 초기화, 포인터 접근, enum과 typedef의 역할 설명하기

이번 글의 학습 목표는 세 가지입니다.

  1. 구조체를 정의하고 값으로 초기화하는 흐름 익히기
  2. 열거형으로 상태 코드를 이름으로 표현해 가독성을 높이기
  3. typedef로 긴 선언을 정리하고 포인터, 함수 시그니처를 읽기 쉽게 만들기

처음 읽을 때는 아래처럼 역할만 먼저 잡아도 충분합니다.

  • struct: 서로 다른 의미의 값을 한 덩어리로 묶음
  • enum: 자주 쓰는 상태값에 이름 붙이기
  • typedef: 긴 선언을 짧게 부르는 별칭 만들기

코드로 따라하기

구조체 정의와 초기화

#include <stdio.h>

struct Student {
    char name[16];
    int grade;
    double average;
};

int main(void) {
    struct Student alice = {"Alice", 2, 88.5};

    printf("name = %s\n", alice.name);
    printf("grade = %d\n", alice.grade);
    printf("average = %.1f\n", alice.average);

    return 0;
}

struct Student 안에는 문자열, 정수, 실수가 함께 들어 있습니다. 중괄호 순서를 구조체 정의와 맞추면 쉽게 초기화할 수 있습니다. 문자열 필드는 고정 길이 배열이므로 printf("%s")와 함께 사용할 수 있습니다.

구조체 포인터와 ->

#include <stdio.h>

struct Point {
    int x;
    int y;
};

void move(struct Point *p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
}

int main(void) {
    struct Point p = {0, 0};
    move(&p, 3, -2);
    printf("(%d, %d)\n", p.x, p.y);
    return 0;
}

구조체 포인터는 (*p).xp->x가 같다는 사실을 기억하면 됩니다. 함수에 구조체를 통째로 복사해 넘기기보다 포인터를 넘기면 값 변경이 원본에 바로 반영되고, 메모리도 절약됩니다.

열거형으로 상태 정의

#include <stdio.h>

enum Status {
    STATUS_OK = 0,
    STATUS_WARNING = 1,
    STATUS_ERROR = 2
};

void print_status(enum Status status) {
    if (status == STATUS_OK) {
        printf("정상\n");
    } else if (status == STATUS_WARNING) {
        printf("주의\n");
    } else {
        printf("오류\n");
    }
}

int main(void) {
    print_status(STATUS_WARNING);
    return 0;
}

열거형은 기본적으로 int이지만, 이름이 붙어 있기 때문에 숫자만 보고 의미를 추측해야 하는 상황을 줄여 줍니다. 값은 0부터 자동 증가하므로 필요할 때만 직접 값을 지정하면 됩니다.

typedef로 선언 간단히 하기

typedef struct {
    char title[32];
    int year;
} Book;

typedef enum {
    LEVEL_BEGINNER,
    LEVEL_INTERMEDIATE,
    LEVEL_ADVANCED
} Level;

int main(void) {
    Book book = {"C Basics", 2026};
    Level level = LEVEL_BEGINNER;

    return 0;
}

typedef는 새로운 자료형을 만드는 것이 아니라, 기존 선언에 별칭을 붙이는 기능입니다. typedef struct { ... } Book;처럼 쓰면 이후에는 Book으로 간단히 선언할 수 있습니다. 열거형도 같은 방식으로 별칭을 붙여 사용합니다.

구조체 변수 자체에는 .를 쓰고, 구조체를 가리키는 포인터에는 ->를 쓴다는 점도 함께 기억해 두세요. 예를 들어 student.namestudent_ptr->name은 비슷한 필드를 읽지만, 앞의 것은 구조체 값 자체이고 뒤의 것은 포인터가 가리키는 구조체입니다.

구조체 배열과 함수 조합

#include <stdio.h>

typedef struct {
    char name[16];
    int score;
} Result;

void print_higher(const Result *results, size_t length, int min_score) {
    size_t i;

    for (i = 0; i < length; i++) {
        if (results[i].score >= min_score) {
            printf("%s: %d\n", results[i].name, results[i].score);
        }
    }
}

int main(void) {
    Result results[3] = {
        {"Ana", 82},
        {"Ben", 76},
        {"Cara", 91}
    };

    print_higher(results, 3, 80);
    return 0;
}

구조체 배열은 "연속된 구조체"라는 점을 제외하면 일반 배열과 동일하게 다룹니다. 포인터 산술과 . 또는 ->를 함께 사용하면 각 요소의 필드를 쉽게 읽을 수 있습니다.

왜 중요한가

구조체와 열거형은 C 코드에서 의미를 담는 기본 단위입니다. 변수만으로는 서로 다른 값의 관계를 설명하기 어렵지만, 구조체를 만들면 프로그램의 데이터 모델이 명확해집니다. 열거형은 숫자 코드 대신 이름을 사용하게 해 주므로 디버깅과 유지보수가 쉬워집니다. 또한 typedef로 긴 선언을 정리하면 헤더 파일과 다른 팀원의 코드를 읽을 때 부담이 크게 줄어듭니다.

CodeSandbox로 이어서 실습하기

아래 샌드박스는 CodeSandbox의 Universal starter입니다. C는 터미널에서 직접 컴파일하고 다시 실행하는 흐름이 중요하니, 이번 글의 코드를 파일로 만들고 빌드-실행 사이클을 다시 따라가 보세요.

Live Practice

C Practice Sandbox

CodeSandbox

Run the starter project in CodeSandbox, compare it with the lesson code, and keep experimenting.

Universal starterCterminal
  1. starter를 fork한 뒤 hello.c 같은 실습 파일을 만든다
  2. 본문 코드를 붙여 넣고 clang 또는 gcc가 있으면 직접 컴파일한다
  3. 코드를 고친 뒤 다시 빌드하고 실행 결과를 비교한다

C는 브라우저 미리보기보다 터미널 빌드 흐름이 핵심입니다. Universal starter의 컴파일러 구성이 환경에 따라 다를 수 있으니, 먼저 clang이나 gcc 사용 가능 여부부터 확인하세요.

실습

  • 따라 하기: struct Student, enum Status 예제를 각각 컴파일해 출력 결과를 확인합니다.
  • 확장하기: typedef structTask를 정의하고, 상태를 enum TaskState로 표현해 간단한 목록을 출력해 보세요.
  • 디버깅: 구조체 포인터를 값으로 넘겼을 때와 포인터로 넘겼을 때의 차이를 printf로 비교합니다.
  • 완료 기준: 구조체 정의, 열거형 정의, typedef 별칭 과정을 각 한 문장으로 설명할 수 있으면 됩니다.

마무리

이번 글에서는 struct, enum, typedef를 이용해 데이터를 구조화하는 방법을 살펴봤습니다. 다음 글에서는 이렇게 정의한 구조체와 함수를 여러 파일로 나누어 관리하기 위해 헤더 파일과 .c 파일을 분리하는 방법을 다룹니다.

💬 댓글

이 글에 대한 의견을 남겨주세요