← C 강의 목록으로
🎯
고급 (Advanced)
struct · typedef · 포인터 · 배열

3단원 — 구조체

서로 관련된 여러 값을 **하나의 자료형**으로 묶는 도구가 구조체입니다. "이름은 string, 나이는 int, 점수는 double" 같은 묶음을 한 변수로 다룹니다.

structtypedef
소요 시간
1~2시간
난이도
📊 중급
선수 조건
🎯 고급 2단원
결과물
여러 값을 묶어 도메인을 표현한다

이 강의에서 배우는 것

  • 1`struct`로 새 자료형을 정의한다.
  • 2`typedef`로 별칭을 부여해 사용성을 높인다.
  • 3구조체 포인터에서 `->` 연산자를 쓴다.
  • 4구조체 배열로 레코드 모음을 관리한다.

왜 구조체가 필요한가? — 변수가 흩어지는 문제

학생 정보를 변수 따로따로 만들면:

c
char  s1_name[32]; int s1_age; double s1_score;
char  s2_name[32]; int s2_age; double s2_score;
/* ... 학생 100명이면? */

배열로 묶어도 자료형이 다르면 한 배열에 못 담습니다. 구조체는 **자료형이 다른 멤버**들을 한 묶음으로 만듭니다.

c
typedef struct {
    char name[32];
    int  age;
    double score;
} Student;

Student arr[100];   // 학생 100명을 한 배열에

핵심 개념

1) 정의와 초기화

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

struct Point p1 = {3, 4};
struct Point p2 = {.x = 1, .y = 2};   // 지정 초기화 (C99)

2) `typedef`로 깔끔하게

c
typedef struct {
    double x;
    double y;
} Point;

Point p = {10, 20};   // struct 키워드 없이 사용

3) 멤버 접근 — `.` vs `->`

c
Point p = {3, 4};
Point *ptr = &p;

p.x;        // 점(.) 연산자: 구조체 변수에서 멤버 접근
ptr->x;     // 화살표(->): 포인터에서 멤버 접근. (*ptr).x 의 단축
text
   p (값)               ptr (포인터)
   ┌──────┐             ┌──────┐
   │ x: 3 │  ◄─────────│ &p   │
   │ y: 4 │             └──────┘
   └──────┘
   p.x = 3              ptr->x = 3

4) 구조체 배열

c
typedef struct { char name[32]; int score; } Student;
Student class_[3] = {
    {"Kim", 90}, {"Lee", 85}, {"Park", 78},
};
class_[1].score;   // 85

각 원소는 독립된 구조체. 멤버 접근은 `arr[i].멤버`.

5) 구조체와 함수

큰 구조체는 값으로 넘기면 통째로 복사되어 비효율적입니다. **포인터로 전달**이 관용:

c
void birthday(Person *p) { p->age++; }
birthday(&someone);

예제로 보기

예제 1 — `ex01_basic.c` : Point 구조체

c
struct Point p1 = {3, 4};
struct Point p2 = {.x = 1, .y = 2};
p1.x = 10;

**실행 결과**

text
p1 = (3, 4)
p2 = (1, 2)
p1.x 변경 후 = (10, 4)

핵심: 멤버는 일반 변수처럼 읽고 쓸 수 있습니다.

예제 2 — `ex02_typedef.c` : 두 점 사이 거리

c
typedef struct { double x, y; } Point;
double distance(Point a, Point b) {
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

**실행 결과**

text
거리: 5.00

핵심: 구조체를 **함수 인자/반환값**으로 그대로 쓸 수 있습니다 (값 전달).

예제 3 — `ex03_struct_array.c` : 학생 명단 평균

c
Student class_[] = {
    {"Kim",  90}, {"Lee",  85}, {"Park", 78}, {"Choi", 92},
};

**실행 결과**

text
Kim    : 90
Lee    : 85
Park   : 78
Choi   : 92
평균: 86.25

핵심: 구조체 배열 + 반복문은 작은 데이터베이스의 시작.

예제 4 — `ex04_pointer.c` : 포인터로 멤버 수정

c
typedef struct { char name[32]; int age; } Person;
void birthday(Person *p) { p->age += 1; }

**실행 결과**

text
생일 전: 홍길동, 20세
생일 후: 홍길동, 21세

핵심: 함수가 호출자의 구조체를 바꾸려면 포인터를 전달해야 합니다.

다른 시각으로 보기 — "이력서" 비유

text
   ┌──────── 이력서(struct Person) ────────┐
   │ 이름:    홍길동                       │
   │ 나이:    21                           │
   │ 키:      175.5                        │
   │ 학생증 사진(...)                       │
   └─────────────────────────────────────┘

이력서 한 장이 한 사람(`Person` 구조체 한 개) 입니다. "이력서 100장 = `Person` 배열 100개"가 됩니다.

`.` vs `->`는 사실 같은 일을 합니다.

c
ptr->x   ≡   (*ptr).x      // 완전히 동치

자주 하는 실수

  1. **`struct` 누락**: `Point p;` 만 쓰면 typedef 안 했을 때 컴파일 오류.

→ 정의에 `typedef`를 함께 쓰는 습관.

  1. **구조체 비교**: `if (p1 == p2)` 는 컴파일 오류. 멤버끼리 비교해야 함.
  2. **큰 구조체 값 전달**: 매 호출마다 통째로 복사 → 포인터 전달 권장.
  3. **포인터에 `.` 사용**: `ptr.x`는 오류. `ptr->x` 또는 `(*ptr).x`.
  4. **패딩(padding)**: `sizeof(Student)`가 멤버 합보다 클 수 있음 (메모리 정렬 때문).

정리

  • 구조체는 자료형이 다른 멤버들을 한 자료형으로 묶는다.
  • `typedef`로 별칭을 만들면 사용성이 좋아진다.
  • 구조체 변수는 `.`, 포인터는 `->`로 멤버에 접근.
  • 큰 구조체는 포인터로 함수에 전달해 복사를 피한다.
  • 구조체 배열은 작은 데이터베이스의 자연스러운 표현.

직접 해 보기

bash
cd src
gcc -std=c11 -Wall -o ex01 ex01_basic.c        && ./ex01
gcc -std=c11 -Wall -o ex02 ex02_typedef.c -lm  && ./ex02
gcc -std=c11 -Wall -o ex03 ex03_struct_array.c && ./ex03
gcc -std=c11 -Wall -o ex04 ex04_pointer.c      && ./ex04

응용:

  • `ex03`에서 평균이 가장 높은 학생의 이름을 함께 출력해 보세요.
  • `ex04`에서 `birthday`를 `void birthday(Person p)` (값 전달)로 바꾸면 결과가 어떻게 달라질까요?

💻 예제 (examples)

실제로 컴파일·실행해 결과를 확인할 수 있는 예제입니다.

ex01_basic.cPoint 구조체
CODE
#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main(void) {
    struct Point p1 = {3, 4};
    struct Point p2 = {.x = 1, .y = 2};

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

    p1.x = 10;
    printf("p1.x 변경 후 = (%d, %d)\n", p1.x, p1.y);
    return 0;
}
▶ 실행 결과
p1 = (3, 4)
p2 = (1, 2)
p1.x 변경 후 = (10, 4)
ex02_typedef.c두 점 사이 거리
CODE
#include <stdio.h>
#include <math.h>

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

double distance(Point a, Point b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

int main(void) {
    Point p = {0, 0};
    Point q = {3, 4};

    printf("거리: %.2f\n", distance(p, q));   /* 5.00 */
    return 0;
}
▶ 실행 결과
거리: 5.00
ex03_struct_array.c학생 명단 평균
CODE
#include <stdio.h>

typedef struct {
    char name[32];
    int  score;
} Student;

int main(void) {
    Student class_[] = {
        {"Kim",  90},
        {"Lee",  85},
        {"Park", 78},
        {"Choi", 92},
    };
    int n = (int)(sizeof(class_) / sizeof(class_[0]));

    int sum = 0;
    for (int i = 0; i < n; i++) {
        printf("%-6s : %d\n", class_[i].name, class_[i].score);
        sum += class_[i].score;
    }
    printf("평균: %.2f\n", (double)sum / n);
    return 0;
}
▶ 실행 결과
Kim    : 90
Lee    : 85
Park   : 78
Choi   : 92
평균: 86.25
ex04_pointer.c포인터로 멤버 수정
CODE
#include <stdio.h>

typedef struct {
    char name[32];
    int  age;
} Person;

void birthday(Person *p) {
    p->age += 1;       /* 화살표로 멤버 접근 */
}

int main(void) {
    Person p = {"홍길동", 20};

    printf("생일 전: %s, %d세\n", p.name, p.age);
    birthday(&p);
    printf("생일 후: %s, %d세\n", p.name, p.age);
    return 0;
}
▶ 실행 결과
생일 전: 홍길동, 20세
생일 후: 홍길동, 21세

📝 과제 (exercises)

직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.

과제 1

문제 1 (hw01.c)

목표: 2D 벡터 구조체 `Vec2 { double x, y; }`를 정의하고 `Vec2 add(Vec2 a, Vec2 b)`, `double dot(Vec2 a, Vec2 b)` 함수를 구현해 사용 예를 출력하세요.

요구사항
  • 파일명: hw01.c
정답 코드 펼치기 / 접기
SOLUTION
#include <stdio.h>

typedef struct {
    double x;
    double y;
} Vec2;

Vec2 add(Vec2 a, Vec2 b) {
    Vec2 r = {a.x + b.x, a.y + b.y};
    return r;
}

double dot(Vec2 a, Vec2 b) {
    return a.x * b.x + a.y * b.y;
}

int main(void) {
    Vec2 a = {1, 2}, b = {3, 4};
    Vec2 c = add(a, b);

    printf("a + b = (%.1f, %.1f)\n", c.x, c.y);
    printf("a . b = %.1f\n", dot(a, b));
    return 0;
}
과제 2

문제 2 (hw02.c)

목표: 학생 명단(이름, 국어, 영어, 수학)을 5명 분량으로 만들고, 각 학생의 **평균**을 출력 후, **평균이 가장 높은 학생**의 이름을 출력하세요.

요구사항
  • 파일명: hw02.c
정답 코드 펼치기 / 접기
SOLUTION
#include <stdio.h>

typedef struct {
    char name[32];
    int kor, eng, math;
} Student;

int main(void) {
    Student arr[5] = {
        {"Kim",  90, 85, 92},
        {"Lee",  78, 88, 70},
        {"Park", 95, 91, 89},
        {"Choi", 60, 70, 65},
        {"Han",  88, 76, 82},
    };

    int top_idx = 0;
    double top_avg = 0;

    for (int i = 0; i < 5; i++) {
        double avg = (arr[i].kor + arr[i].eng + arr[i].math) / 3.0;
        printf("%-6s 평균 %.2f\n", arr[i].name, avg);
        if (avg > top_avg) { top_avg = avg; top_idx = i; }
    }

    printf("\n최고 평균: %s (%.2f)\n", arr[top_idx].name, top_avg);
    return 0;
}
과제 3

문제 3 (hw03.c)

목표: 직사각형 구조체 `Rect { int w, h; }`에 대해 `int area(const Rect *r)`, `int perimeter(const Rect *r)`를 구현하세요. **구조체 포인터** 사용을 연습합니다.

요구사항
  • 파일명: hw03.c
정답 코드 펼치기 / 접기
SOLUTION
#include <stdio.h>

typedef struct {
    int w;
    int h;
} Rect;

int area(const Rect *r)      { return r->w * r->h; }
int perimeter(const Rect *r) { return 2 * (r->w + r->h); }

int main(void) {
    Rect r = {5, 3};
    printf("넓이:  %d\n", area(&r));
    printf("둘레:  %d\n", perimeter(&r));
    return 0;
}
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗