말랑말랑제리스타일

C언어 구조체 동적 할당을 이용한 학생 관리 프로그램 본문

직접만든프로그램 소개

C언어 구조체 동적 할당을 이용한 학생 관리 프로그램

제리제리 2023. 7. 26. 17:00

지난 글에서 C언어 구조체를 이용한 학생 관리 프로그램을 만들어봤습니다. 사실 Realloc을 사용해 가면서 메모리를 동적으로 할당하는 경우 메모리 관련 에러가 자주 발생해서 최대 학생 수를 선언해 두고 시작했는데요.

이번에는 malloc, realloc을 이용해 배열이 가득 찬 경우 메모리를 동적으로 할당해서 배열 크기를 늘려주는 프로그램을 만들어봤습니다.

C언어에서 구조체 메모리 동적할당하는 데 사용되는 함수

C언어에서 구조체 배열을 동적 할당할 때 사용되는 함수로는 일단 malloc, calloc, realloc 세가지가 있습니다.

malloc 함수

malloc은 단순히 메모리를 할당하는 기능을 가진 C언어 함수로 stdlib.h에 위치하고 있습니다.

void 포인터를 리턴하고 매개변수로 사이즈가 사용됩니다.

즉 원형이 이런 형태라는거죠.

void* malloc(size_t size)

자 이 함수를 이용해서 최초에 메모리를 할당해 줄 수 있습니다.

이걸 이용해서 구조체에 동적 할당하는 예를 만들어보겠습니다.

typedef struct student {
  int snum;
  char sname[MAX_NAME_LEN];
} student;

이런 학생이라는 구조체가 있다고 가정합니다.

참고로 여기서 typedef를 써준 이유는 이후에 이 구조체를 사용할 때 typedef가 없으면 struct student라고 써야 되지만 typedef를 써줌으로 인해 struct student가 아닌 student로 축약해서 사용할 수 있기 때문입니다.

모리 동적 할당이 들어가는 경우 이 구조체 명을 자주 써야 되기 때문에 typedef를 사용하는 게 훨씬 편하다는 거죠.

 

이런 구조체가 있다고 할 때 메인 함수에서는 구조체 포인터를 만들어줘야겠죠.

  student *students;
  
  students = (student *)malloc(sizeof(student) * 10);

이런 식으로 student 포인터 형의 students라는 변수를 선언해 주고 malloc 함수를 이용해서 구조체 사이즈 * 10만큼의 크기를 할당해 줍니다. 마지막으로 void 포인터형의 리턴을 student 포인터형으로 변환시킴으로써 student 포인터형 배열이 되는 거죠.

이걸 동적 할당을 없애고 표현하면 아래와 같습니다.

student students[10]

확실히 동적 할당을 사용하면 많이 복잡해지긴 하죠.

calloc 함수

사실 calloc 함수는 malloc 함수와 크게 차이는 없습니다. malloc 함수를 개량한 형태로 void 포인터를 리턴하는 건 동일하지만 매개변수로 항목의 개수와 항목의 사이즈를 받는다는 점이 다르죠. 

추가로 calloc으로 초기화를 할 경우 모든 배열의 값을 초기화시켜주기도 합니다.

함수 원형은 아래와 같은 형태겠죠.

void* calloc(size_t elt_count, size_t elt_size)

elt_count에는 배열 항목의 개수, elt_size에는 각 항목의 크기가 들어갈 겁니다.

즉, 상단의 malloc을 calloc으로 바꿔주면 아래 소스와 같겠죠.

  student *students;
  students = (student *)calloc(10, sizeof(student));

근데 사실상 calloc은 잘 안 쓰는 걸로 알고 있고 저도 잘 쓰지는 않습니다.

realloc 함수

자 마지막으로 소개할 구조체를 동적할당하기 위한 함수는 realloc입니다.

malloc으로 10만큼 할당된 배열이 크기가 부족해서 다시 malloc으로 20만큼 동적 할당할 경우 기존에 들어있던 값들은 찾을 수가 없게 됩니다.

. 따라서 realloc이라는 함수를 사용해서 기존의 값들을 유지하면서 크기만 동적으로 늘려주는 기능을 사용하게 되는데요.

매개변수로 기존의 배열 명, 변경될 사이즈를 넣게 됩니다.

그럼 이번에는 realloc 함수의 원형을 보시죠.

void* realloc(void* memblock, size_t size)

memblock에는 기존의 배열 명을 넣어주고 size에는 변경될 크기를 넣어주면 된다는 거죠.

예를 들어 학생 수가 최초에 10으로 할당되었는데 20으로 바뀌어야 한다면 아래와 같이 써볼 수 있습니다.

students = (student *)realloc(students, sizeof(student) 20);

이게 사실 배열의 메모리 크기만 늘려주는 것처럼 보이지만 실제로는 또 다른 위치에 메모리를 할당해 주기 때문에 너무 많이 사용하면 메모리 관련 에러가 발생할 수 있으니 가능하면 너무 많이 사용하지 않는 것을 권고합니다.

 

구조체 동적 할당을 사용한 C언어 학생 관리 프로그램

지금까지 구조체를 메모리 동적할당하는 함수 세 가지를 설명했고 이번에는 기존의 학생 관리 프로그램에 적용해 보겠습니다.

기존의 학생 관리 프로그램 소스는 아래 글을 참고해 주세요.

2023.06.26 - [직접만든프로그램 소개] - c언어 구조체로 학생 관리 콘솔 프로그램 만들기

 

c언어 구조체로 학생 관리 콘솔 프로그램 만들기

C언어에서 구조체를 이용해 학생 관리 프로그램을 만들어봅시다. 콘솔 프로그래밍으로 학생 배열을 만들어 진행할 예정이며, 기능은 입력, 전체 출력, 번호를 이용한 삭제, 번호를 이용한 조회

jerry-style.tistory.com

 

앞서 사용할 함수 설명은 끝났지만 다시 한번 기존 소스에서 변경할 점을 알려드릴게요.

#define INCREASE_STUDENT 10

일단 기존의 MAX_STUDENT 100으로 잡혀있던 것을 INCREASE_STUDENT 10로 변경해서 최초 10명을 할당하고 배열 최대 학생 수를 넘어갈 경우 다시 10명을 더 할당해 주는 방식으로 할 예정입니다.

typedef struct student {
  int snum;
  char sname[MAX_NAME_LEN];
} student;

다음으로 앞서 설명했지만 동적 할당을 사용하는 경우 구조체 명을 간간히 써줘야 하기 때문에 typedef를 달아서 사용이 편리하도록 수정해 줍니다.

  // struct student students[MAX_STUDENT];
  student *students;
  int increased = 1;
  students = (student *)malloc(sizeof(student) * INCREASE_STUDENT);

이제 기존의 students 배열로 선언했던 부분을 주석처리하고 students를 student 포인터로 선언을 해줍니다.

여기서 increased라는 변수를 추가한 이유는 최초에 10명을 할당하고 이걸 증가시켜서 두 번째에는 20명, 세 번째에는 30명 이런 식으로 동적 메모리를 할당하기 위함이죠.

그리고 앞서 설명한 malloc 함수를 사용해 10명짜리 학생 배열을 동적으로 할당해 줍니다.

else if (menu == 1) {
      printf("===학생 입력===\n");
      printf("학생 번호: ");
      scanf("%d", &students[num_of_student].snum);
      printf("학생 이름: ");
      scanf("%s", &students[num_of_student].sname);
      num_of_student++;
      if (num_of_student >= increased * INCREASE_STUDENT) {
        increased++;
        students = (student *)realloc(students, sizeof(student) * increased * INCREASE_STUDENT);
      }

마지막으로 학생 수가 배열의 최대를 넘어서는 게 가능한 경우는 학생 입력 메뉴겠죠.

학생을 입력한 뒤 학생 수가 현재 배열의 크기(증가된 횟수 * 학생 증가량(10))를 넘어서는 경우 증가된 횟수를 늘려서 학생 구조체 10명 치 공간을 realloc 함수를 이용해 동적으로 더 할당해 주는 겁니다.

C언어 구조체 동적 할당을 이용한 학생 관리 프로그램 최종 소스

C언어 구조체 동적 할당을 이용한 학생 관리 프로그램을 상단과 같이 수정하고 나명 최종 소스는 아래와 같아집니다.

#include <stdio.h>
#define MAX_NAME_LEN 100
#define INCREASE_STUDENT 10

typedef struct student {
  int snum;
  char sname[MAX_NAME_LEN];
} student;

int main(void) {
  // struct student students[MAX_STUDENT];
  student *students;
  int increased = 1;
  students = (student *)malloc(sizeof(student) * INCREASE_STUDENT);
  //동적 할당 수정
  int num_of_student = 0;
  while (1) {
    int menu = 0;
    printf("=====메뉴=====\n");
    printf("1. 학생 입력\n");
    printf("2. 학생 리스트 출력\n");
    printf("3. 학생 번호 검색\n");
    printf("4. 학생 삭제\n");
    printf("5. 프로그램 종료\n");
    printf("메뉴 번호를 입력하세요 : ");
    scanf("%d", &menu);
    if (menu < 1 || menu > 5) {
      printf("잘못된 입력입니다.\n");
      continue;
    }
    if (menu == 5) {
      free(students);
      return 0;
    } else if (menu == 1) {
      printf("===학생 입력===\n");
      printf("학생 번호: ");
      scanf("%d", &students[num_of_student].snum);
      printf("학생 이름: ");
      scanf("%s", &students[num_of_student].sname);
      num_of_student++;
      if (num_of_student >= increased * INCREASE_STUDENT) {
        increased++;
        students = (student *)realloc(students, sizeof(student) * increased * INCREASE_STUDENT);
      }
    } else if (menu == 2) {
      printf("===학생 리스트 출력===\n");
      for (int i = 0; i < num_of_student; i++) {
        printf("학생 번호 : %d 학생 이름 : %s\n", students[i].snum,
               students[i].sname);
      }
    } else if (menu == 3) {
      int snum;
      int exists = 0;
      printf("===학생 번호 검색===\n");
      printf("학생 번호: ");
      scanf("%d", &snum);
      for (int i = 0; i < num_of_student; i++) {
        if (students[i].snum == snum) {
          printf("학생 번호 : %d 학생 이름 : %s\n", students[i].snum,
                 students[i].sname);
          exists++;
          break;
        }
      }
      if (exists == 0) {
        printf("학생번호 %d 이 존재하지 않습니다.", snum);
      }
    } else if (menu == 4) {
      int snum;
      int exists = 0;
      printf("===학생 삭제===\n");
      printf("학생 번호: ");
      scanf("%d", &snum);
      for (int i = 0; i < num_of_student; i++) {
        if (students[i].snum == snum) {
          printf("학생 번호 : %d 학생 이름 : %s 이 삭제됩니다.\n",
                 students[i].snum, students[i].sname);
          for (int j = i; j < num_of_student; j++) {
            students[j] = students[j + 1];
          }
          num_of_student--;
          exists++;
          break;
        }
      }
      if (exists == 0) {
        printf("학생번호 %d 이 존재하지 않습니다.\n", snum);
      }
    }
  }
  return 0;
}

상단 소스의 결과 화면
상단 소스의 결과 화면

여기서 유일하게 설명 안 한 게 free인데, 앞서 말한 것처럼 realloc의 경우 안 그래도 에러가 많이 떨어지기 때문에 마지막에 할당 해제정도는 꼭 해주도록 합시다.

반응형
Comments