[★C] 구조체 (2)

방법이있지·2025년 6월 10일
post-thumbnail

구조체는 자기 자신과 동일한 타입의 구조체를 가리키는 포인터 를 포함할 수 있습니다. 이를 활용하여 연결 리스트와 같은 자료구조를 구현할 수 있습니다.

구조체 배열

  • 동일한 형태의 구조체 변수는 배열로 저장할 수 있음
  • 구조체 배열의 이름은 첫 번째 요소의 주소를 나타냄
    • 즉 구조체 배열을 함수에서 처리하려면, 구조체 포인터를 매개변수로 받아야 함
#include <stdio.h>

struct song         // 음악 정보를 저장할 구조체
{
    char *title;
    char *singer;
    int year;
};

void print_playlist(struct song *s);
// struct song * 형의 포인터를 매개변수로 받는 함수

int main(void){
    struct song playlist[5] = {
        {"파노라마", "이찬혁", 2022},
        {"Hello Mr. My Yesterday", "애쉬그레이", 2010},
        {"센치해", "WINNER", 2016},
        {"Autumn Dream", "엔플라잉", 2019},
        {"라일락", "아이유", 2021}
    };

    print_playlist(playlist); // 배열명을 함수에 전달
    return 0;
}

void print_playlist(struct song *s){
    for (int i = 0; i < 5; i++){
        // (s + i): 배열요소의 주소
        // ->로 각 멤버에 접근
        printf("%s - %s (%d)\n", (s + i) -> title, (s + i) -> singer, (s + i) -> year);
    }
}

// [출력]
// 파노라마 - 이찬혁 (2022)
// Hello Mr. My Yesterday - 애쉬그레이 (2010)
// 센치해 - WINNER (2016)
// Autumn Dream - 엔플라잉 (2019)
// 라일락 - 아이유 (2021)
  • 배열명 playlist는 배열의 첫 번째 요소의 주소로, struct song 구조체 변수를 가리킴
  • print_playlist 함수는 struct song *형 포인터 매개변수 s를 통해 배열을 참조
  • 이후 포인터 연산 (s + i)로 각 배열 요소의 주소 접근
    • 이후 (s + i) -> 멤버로 구조체의 멤버에 접근
    • s[i].멤버, *(s + i).멤버도 동일 기능 수행

자기 참조 구조체

  • 자신과 동일한 구조체 타입을 가리키는 포인터를 멤버로 가진 구조체

예제: 연결 리스트

#include <stdio.h>

// 자기 참조 구조체: 연결리스트의 노드
struct node {
    int value;          // 값을 저장
    struct node *next;  // 다음 노드를 가리킴
};
  • struct node 구조체는, 자신의 타입인 다른 struct node를 가리킬 수 있는 포인터 next를 포함
// 위 코드에서 계속
int main(void){
  struct node n1 = {41, 0}, n2 = {9, 0}, n3 = {33, 0};
  struct node *head = &n1;    // 첫 노드를 가리키는 포인터

  // n1의 next 멤버는 n2를, n2의 next 멤버는 n3를 가리킴
  n1.next = &n2;
  n2.next = &n3;

  // head가 가리키는 n1의 value 출력
  printf("head의 값: %d\n", head -> value);
  // head로 n2의 value 출력
  printf("head.next의 값: %d\n", head -> next -> value);

  // [출력]
  // head의 값: 41
  // head.next의 값: 9

  // 아래 코드에서 계속

  • head는 첫 노드 n1을 가리킴
  • n1.nextn2를, n2.nextn3를 가리킴
  • 즉 첫 노드만 알고 있다면, next 포인터를 따라가며 나머지 노드를 사용할 수 있음
  // 위 코드에서 main 함수 계속
  struct node *curr = head;   // 현재 노드를 가리키는 포인터

  // 모든 노드의 값을 출력
  while (curr != NULL){
      printf("%d ", curr -> value);   // curr가 가리키는 노드의 value 출력
      curr = curr -> next;            // curr가 다음 노드를 가리키게 함
  }

  // [출력]
  // 41 9 33

  return 0;
}

  • 현재 노드를 가리키는 포인터 curr를 사용
    • 처음엔 head와 동일하게 n1을 가리킴
  • curr = curr -> next를 반복하며 다음 노드로 이동
  • 마지막 노드 n3next 값은 0
    • 이 값을 curr에 저장하면 curr은 널 포인터가 되어 반복 종료

typedef: 형 재정의

  • 매번 구조체를 struct 예약어와 함께 쓰는 것은 불편
  • typedef struct 구조체이름 새_자료형_이름
    • 앞으로 struct 구조체이름 대신 새자료형이름 사용 가능
#include <stdio.h>

struct price {
    int mandu;      // 만두의 가격
    int soondae;    // 순대의 가격
    int hotteok;    // 호떡의 가격
};

// struct price 형을 Price 형으로 재정의
typedef struct price Price;

int main(void){
    Price p1 = {5000, 6000, 3000};   // Price형으로 초기화
    Price *pp = &p1;                 // Price *형 포인터 선언, 초기화
    printf("만두 가격: %d\n", (*pp).mandu);
    printf("순대 가격: %d\n", pp -> soondae);
    printf("호떡 가격: %d\n", pp -> hotteok);

    // [출력]
    // 만두 가격: 5000
    // 순대 가격: 6000
    // 호떡 가격: 3000
    return 0;
}
  • 아래와 같이 구조체를 선언하면서, 바로 재정의할 수도 있음
typedef struct price {
    int mandu;      // 만두의 가격
    int soondae;    // 순대의 가격
    int hotteok;    // 호떡의 가격
} Price;

연습문제

학생 5명의 국어, 영어, 수학 점수를 입력해 총점, 평균, 학점을 구하고 총점 순으로 정렬해 출력합니다. 학점은 평균이 90점 이상이면 A, 80점 이상이면 B, 70점 이상이면 C, 그 외는 F로 평가합니다.

  • 학생 정보를 struct student -> Student형 구조체로 관리
  • Student형 구조체 배열을 선언해 5명의 학생 정보를 저장
  • 입력 출력 정렬 값변경 함수를 따로 만듦 - 매개변수는 구조체 포인터, 주소를 받음
  • Selection Sort를 이용해 내림차순 정렬
#include <stdio.h>

typedef struct student {
    int id;         // 학번
    char name[10];     // 이름
    int scores[3];  // 국, 영, 수 점수
    int sum;        // 총점
    double mean;    // 평균
    char grade;     // 학점
} Student;

void input_data(Student *);    // 학생 정보 입력
void sort_data(Student *);     // 총점순으로 내림차순 정렬
void swap(Student *ps1, Student *ps2);  // 두 변수의 값 변경
void output_data(Student *);   // 학생 정보 출력


int main(void){
    Student s_list[5];
    input_data(s_list);
    printf("# 정렬 전 데이터...\n");
    output_data(s_list);
    sort_data(s_list);
    printf("# 정렬 후 데이터...\n");
    output_data(s_list);
}

void input_data(Student *ps){
    for (int i = 0; i < 5; i++){
        printf("학번: ");
        scanf("%d", &(ps->id));
        printf("이름: ");
        scanf("%s", ps->name);
        printf("국어, 영어, 수학 점수: ");

        ps->sum = 0;
        for (int j = 0; j < 3; j++){
            scanf("%d", &(ps->scores[j]));
            ps->sum += (ps->scores)[j];
        }

        ps->mean = ps->sum / 3.0;

        if (ps->mean >= 90.0){
            ps->grade = 'A';
        } else if (ps->mean >= 80.0){
            ps->grade = 'B';
        } else if (ps->mean >= 70.0){
            ps->grade = 'C';
        } else {
            ps->grade = 'F';
        }

        ps++;
    }
}

// Selection Sort
void sort_data(Student *ps){
    for (int i = 0; i < 5; i++){
        int l_idx = i;
        for (int j = i + 1; j < 5; j++){
            if (ps[j].sum > ps[l_idx].sum){
                l_idx = j;
            }
        }
        swap(ps + i, ps + l_idx);
    }

}

void swap(Student *ps1, Student *ps2){
    Student temp;
    temp = *ps1;
    *ps1 = *ps2;
    *ps2 = temp;
}

void output_data(Student *ps){
    for (int i = 0; i < 5; i++){
        printf("%4d %s ", ps->id, ps->name);

        for (int j = 0; j < 3; j++){
            printf("%4d ", ps->scores[j]);
        }

        printf("%5d %.1lf %c\n", ps->sum, ps->mean, ps->grade);

        ps++;
    }
}

// [입력]
// 학번: 315
// 이름: 홍길동
// 국어, 영어, 수학 점수: 80 75 90
// 학번: 316
// 이름: 이순신
// 국어, 영어, 수학 점수: 88 92 100
// 학번: 317
// 이름: 서하윤
// 국어, 영어, 수학 점수: 95 99 98
// 학번: 318
// 이름: 유관순
// 국어, 영어, 수학 점수: 84 70 72
// 학번: 319
// 이름: 박신혜
// 국어, 영어, 수학 점수: 60 65 40

// [출력]
// # 정렬 전 데이터...
//  315 홍길동   80   75   90   245 81.7 B
//  316 이순신   88   92  100   280 93.3 A
//  317 서하윤   95   99   98   292 97.3 A
//  318 유관순   84   70   72   226 75.3 C
//  319 박신혜   60   65   40   165 55.0 F
// # 정렬 후 데이터...
//  317 서하윤   95   99   98   292 97.3 A
//  316 이순신   88   92  100   280 93.3 A
//  315 홍길동   80   75   90   245 81.7 B
//  318 유관순   84   70   72   226 75.3 C
//  319 박신혜   60   65   40   165 55.0 F
profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

0개의 댓글