C & C++/디딤돌 C언어

Part 22. 사용자 정의 형식(구조체, 공용체, 열거형) 실습

언휴 2024. 1. 17. 14:34

Part 22. 사용자 정의 형식(구조체, 공용체, 열거형) 실습

사용자 정의 형식(구조체, 공용체, 열거형) 실습

77. 사용자 정의 실습 – 시나리오

프로그램을 작성할 때 사용자 정의 형식을 기준으로 소스 파일을 구분하여 개발하면 유지보수 비용을 줄일 수 있어요.

사용자 정의 형식을 헤더에 정의하고 해당 형식의 멤버에 영향을 주는 기능은 소스 파일에 정의하는 형태로 개발하면 개발 비용 뿐만 아니라 C++이나 Java, C#과 같은 OOP 언어를 익히는데도 도움을 줄 수 있죠.

여기에서는 학생을 사용자 정의 형식으로 정의하고 관련 기능을 소스로 정의하여 사용하는 실습을 진행할게요.

실습 시나리오

학생은 학번, 이름, 체력, 지력, 스트레스, 연속으로 공부한 횟수를 멤버로 갖는다.
학번은 순차적으로 부여하며 이름은 생성할 때 입력인자로 전달받는다.
체력은 초기값이 100이며 0에서 100 사이의 값을 유지한다.
지력은 초기값이 100이며 0에서 200 사이의 값을 유지한다.
스트레스는 초기값이 0이며 0에서 100 사이의 값을 유지한다.
연속으로 공부한 횟수는 초기값이 0이며 0에서 5 사이의 값을 유지한다.
학생이 공부하면 연속으로 공부한 횟수는 1 증가하며 그 외의 행동을 하면 0으로 리셋한다.

학생이 공부하면 체력이 5 소모하며 지력은 연속으로 공부한 횟수만큼 증가하고 스트레스는 2 감소한다.
학생이 강의를 받으면 체력은 3 소모하며 지력은 연속으로 공부한 횟수만큼 증가하고 스트레스는 2 증가한다.

학생이 잠을 자면 체력은 10 회복하고 스트레스는 5 감소한다.
학생이 휴식하면 체력은 3회복하고 스트레스는 10 감소한다.

학생이 음료를 마시면 체력이 10 소모하며 지력은 5 감소하고 스트레스는 2 감소한다.
학생이 노래하면 체력이 10 소모하며 지력은 1 증가하고 스트레스는 5 감소한다.

추가로 학생 정보를 출력하는 기능을 제공한다.

78. 사용자 정의 형식 실습 – 형식 정의

먼저 학생 구조체와 상수를 정의하세요.
소스 파일을 분리하여 개발할 때 다른 소스에서 접근할 수 있는 형식과 기능은 헤더 파일에 작성하세요.
그리고 구체적인 함수 정의문을 소스 파일에 작성하세요.

학생 형식을 정의할 헤더 파일은 Student.h 라고 정할게요.

학생은 이름, 학번, 체력, 지력, 스트레스, 연속으로 공부한 횟수를 멤버로 가져야겠죠.

#define MAX_NAME_LEN    20
typedef struct _Student Student;
struct _Student
{
    char name[MAX_NAME_LEN+1];//이름
    int num; //번호
    int hp; //체력
    int iq; //지력
    int stress; //스트레스
    int scnt; //연속으로 공부한 횟수
};

그리고 체력, 지력, 스트레스, 연속으로 공부한 횟수는 값의 범위가 있어요.
여기에서는 이 부분을 열거형으로 정의할게요.
물론 매크로 상수로 정의할 수도 있어요.

enum _StuConst
{
   MIN_HP=0, MAX_HP=100, DEF_HP=100,
   MIN_IQ=0, MAX_IQ=200, DEF_IQ=100,
   MIN_STRESS=0, MAX_STRESS=100, DEF_STRESS=0,
   MIN_SCNT=0, MAX_SCNT=5, DEF_SCNT=0
};

다른 소스 파일에서 학생의 기능을 호출하여 사용할 수 있게 함수를 선언하세요.

프로그램에 여러 개의 형식이 있을 때 어떤 형식에 관한 함수인지 판별하기 쉽게 함수 이름을 정하세요.
여기에서는 함수 이름의 앞 부분은 사용자 정의 형식 이름을 추가하기로 할게요.
예를 들어 “학생이 공부하다.” 기능은 Student_Study로 정의하는 것이죠.
즉 언더바 앞쪽은 형식 이름이고 뒤쪽은 기능으로 볼 수 있겠죠.

학생 데이터를 사용하기 위해 초기 설정하는 함수를 생성자라 부를게요.
그리고 생성자 함수는 Student_Student처럼 기능 부분도 형식 이름으로 정의할게요.
시나리오에 학생을 생성할 때 이름을 전달받아 설정하는 것으로 나와있네요.
이에 맞게 생성자의 함수 원형을 결정하세요.

void Student_Student(Student *stu,const char *name); //생성자

그리고 학생에 관한 기능은 언제나 첫번째 인자는 어느 학생 데이터가 있는 메모리 주소를 전달받게 하세요.
즉 Student 포인터 형식을 받는 것이죠.

“공부하다.”, “강의받다.”, “휴식하다.”, “잠자다.”, “음료를 마시다.”, “노래하다.” “정보 출력” 기능이 필요하죠.

void Student_Study(Student *stu);//공부하다.
void Student_ListenLecture(Student *stu);//강의받다.
void Student_Relax(Student *stu);//휴식하다.
void Student_Sleep(Student *stu);//잠자다.
void Student_Drink(Student *stu);//음료를 마시다.
void Student_Sing(Student *stu);//노래하다.
void Student_View(Student *stu);//자신의 정보를 출력하다.

◈ Student.h

#pragma once
 
#define MAX_NAME_LEN    20
typedef struct _Student Student;
struct _Student
{
    char name[MAX_NAME_LEN+1];//이름
    int num; //번호
    int hp; //체력
    int iq; //지력
    int stress; //스트레스
    int scnt; //연속으로 공부한 횟수
};
enum _StuConst
{
    MIN_HP=0, MAX_HP=100, DEF_HP=100,
    MIN_IQ=0, MAX_IQ=200, DEF_IQ=100,
    MIN_STRESS=0, MAX_STRESS=100, DEF_STRESS=0,
    MIN_SCNT=0, MAX_SCNT=5, DEF_SCNT=0
};
 
void Student_Student(Student *stu,const char *name); //생성자
void Student_Study(Student *stu);//공부하다.
void Student_ListenLecture(Student *stu);//강의받다.
void Student_Relax(Student *stu);//휴식하다.
void Student_Sleep(Student *stu);//잠자다.
void Student_Drink(Student *stu);//음료를 마시다.
void Student_Sing(Student *stu);//노래하다.
void Student_View(Student *stu);//자신의 정보를 출력하다.

79. 사용자 정의 형식 실습 – 기능 구현

이제 Student.c 소스 파일을 프로젝트에 추가하여 구체적으로 기능을 정의하세요.

먼저 필요한 헤더 파일을 포함하는 구문이 필요하겠죠.

#include "Student.h"
#include <memory.h>
#include <string.h>
#include <stdio.h>

먼저 학생 생성자를 작성하세요.
생성자는 학생 데이터를 초기 설정하는 함수예요.

void Student_Student(Student *stu,const char *name)
{
 학생은 생성할 때 순차적으로 번호를 부여하세요.
 이를 위해 정적 변수로 가장 최근에 부여한 학생 번호를 기억하는 변수를 선언하세요.
    static int last_num;

 입력 인자로 전달받은 학생의 메모리를 초기화가 필요하죠.
    memset(stu,0,sizeof(Student));
 그리고 입력 인자로 전달받은 이름을 설정하세요.
    strncpy_s(stu->name,sizeof(stu->name),name,MAX_NAME_LEN);
 나머지 멤버들도 초기값으로 설정하세요.
    stu->hp = DEF_HP;
    stu->iq = DEF_IQ;
    stu->stress = DEF_STRESS;
    stu->scnt = DEF_SCNT;
 가장 최근에 부여한 학생 번호를 1 증가한 후에 학생 번호를 설정하세요.
    last_num++;
    stu->num = last_num;
}

이번에는 “공부하다.” 기능을 구현합시다.

void Student_Study(Student *stu)
{
 먼저 누가 공부하는지 콘솔 화면에 출력하세요.
    printf("<%d>:%s 공부하다.",stu->num, stu->name);
 그리고 시나리오에 나온 것처럼 학생의 능력치를 변경하세요.
    //학생이 공부하면 체력이 5 소모하며 지력은 연속으로 공부한 횟수만큼 증가하고 스트레스는 2 감소한다.
 주의할 점은 능력치가 최대값과 최소값의 범위를 벗어나지 않게 제어하세요.
 이러한 부분은 다른 기능에서도 공통적으로 수행해야 하므로 각 능력치를 설정하는 함수를 별도로 제공하여 구현하면 좋아요.
    Student_SetHP(stu, stu->hp -5);
    Student_SetIQ(stu, stu->iq + stu->scnt);
    Student_SetStress(stu, stu->stress - 2);
    Student_IncreSCnt(stu);
}

학생의 능력치를 설정하는 함수는 Student.c 소스 파일에서만 접근할 함수들이죠.
이들 함수의 선언문은 헤더 파일이 아닌 Student.c 소스의 #include 구문 아래에 작성하세요.

void Student_SetHP(Student *stu, int hp);
void Student_SetIQ(Student *stu, int iq);
void Student_SetStress(Student *stu, int stress);
void Student_IncreSCnt(Student *stu);

같은 방법으로 “강의받다.” 기능을 구현하세요.

void Student_ListenLecture(Student *stu)
{
    printf("<%d>:%s 강의받다. \n",stu->num, stu->name);
    //학생이 강의를 받으면 체력은 3 소모하며 지력은 연속으로 공부한 횟수만큼 증가하고 스트레스는 2 증가한다.
    Student_SetHP(stu, stu->hp -3);
    Student_SetIQ(stu, stu->iq + stu->scnt);
    Student_SetStress(stu, stu->stress + 2);
    stu->scnt = 0;
}

나머지 기능도 같은 방법으로 구현하세요.

void Student_Relax(Student *stu)
{
    printf("<%d>:%s 류식하다. \n",stu->num, stu->name);
    //학생이 휴식하면 체력은 3회복하고 스트레스는 10 감소한다.
    Student_SetHP(stu, stu->hp +3);
    Student_SetStress(stu, stu->stress -10);
    stu->scnt = 0;
}
void Student_Sleep(Student *stu)
{
    printf("<%d>:%s 잠을자다. \n",stu->num, stu->name);
    //학생이 잠을 자면 체력은 10 회복하고 스트레스는 5 감소한다.
    Student_SetHP(stu, stu->hp + 10);
    Student_SetStress(stu, stu->stress - 5);
    stu->scnt = 0;
}
void Student_Drink(Student *stu)
{
    printf("<%d>:%s 음료를 마시다. \n",stu->num, stu->name);
    //학생이 음료를 마시면 체력이 10 소모하며 지력은 5 감소하고 스트레스는 2 감소한다.
    Student_SetHP(stu, stu->hp -10);
    Student_SetIQ(stu, stu->iq - 5);
    Student_SetStress(stu, stu->stress - 2);
    stu->scnt = 0;
}
void Student_Sing(Student *stu)
{
    printf("<%d>:%s 노래하다. \n",stu->num, stu->name);
    // 학생이 노래하면 체력이 10 소모하며 지력은 1 증가하고 스트레스는 5 감소한다.
    Student_SetHP(stu, stu->hp -10);
    Student_SetIQ(stu, stu->iq + 1);
    Student_SetStress(stu, stu->stress - 5);
    stu->scnt = 0;
}

학생 정보를 출력하는 함수를 작성하세요.

void Student_View(Student *stu)
{
 학생 번호와 이름은 중요한 데이터이므로 먼저 출력하는 것이 좋겠죠.
    printf("<%d>:%s\n",stu->num, stu->name);
 그리고 나머지 능력치를 출력하세요.
 굳이 연속으로 공부한 횟수는 출력할 필요가 없겠죠.
 모든 데이터를 최종 사용자에게 알려줄 필요는 없어요.
    printf("\t 체력:%d 아이큐:%d 스트레스:%d\n",stu->hp, stu->iq,stu->stress);
}

이제 각 능력치를 변경하는 함수를 작성하세요.

void Student_SetHP(Student *stu, int hp)
{
 입력 인자로 전달받은 값이 최대값을 벗어나면 최대값으로 수정하세요.
    if(hp > MAX_HP)
    {
        hp = MAX_HP;
    }
 마찬가지로 최소값을 벗어나면 최소값으로 수정해야겠죠.
    if(hp < MIN_HP)
    {
        hp = MIN_HP;
    }
 이제 수정한 값으로 학생의 hp를 설정하세요.
    stu->hp = hp;
}

아이큐와 스트레스를 설정하는 함수도 같은 방법으로 구현하세요.

void Student_SetIQ(Student *stu, int iq)
{
    if(iq > MAX_IQ)
    {
        iq = MAX_IQ;
    }
    if(iq < MIN_IQ)
    {
        iq = MIN_IQ;
    }
    stu->iq = iq;
}
void Student_SetStress(Student *stu, int stress)
{
    if(stress > MAX_STRESS)
    {
        stress = MAX_STRESS;
    }
    if(stress < MIN_STRESS)
    {
        stress = MIN_STRESS;
    }
    stu->stress = stress;
}

연속으로 공부한 회수를 증가하는 함수를 작성하세요.

void Student_IncreSCnt(Student *stu)
{
 학생의 scnt 값이 최대값보다 작을 때만 1 증가하게 하세요.
    if(stu->scnt < MAX_SCNT)
    {
        stu->scnt++;
    }
}

◈ Student.c

#include "Student.h"
#include <memory.h>
#include <string.h>
#include <stdio.h>
void Student_SetHP(Student *stu, int hp);
void Student_SetIQ(Student *stu, int iq);
void Student_SetStress(Student *stu, int stress);
void Student_IncreSCnt(Student *stu);
void Student_Student(Student *stu,const char *name)
{
    static int last_num;    
 
    memset(stu,0,sizeof(Student));
    strncpy(stu->name,name,MAX_NAME_LEN);
    stu->hp = DEF_HP;
    stu->iq = DEF_IQ;
    stu->stress = DEF_STRESS;
    stu->scnt = DEF_SCNT;
 
    last_num++;
    stu->num = last_num;
}
void Student_Study(Student *stu)
{
    printf("<%d>:%s 공부하다. \n",stu->num, stu->name);
    //학생이 공부하면 체력이 5 소모하며 지력은 연속으로 공부한 횟수만큼 증가하고 스트레스는 2 감소한다. 
    Student_SetHP(stu, stu->hp -5);
    Student_SetIQ(stu, stu->iq + stu->scnt);
    Student_SetStress(stu, stu->stress - 2);
    Student_IncreSCnt(stu);
}
 
void Student_ListenLecture(Student *stu)
{
    printf("<%d>:%s 강의받다. \n",stu->num, stu->name);
    //학생이 강의를 받으면 체력은 3 소모하며 지력은 연속으로 공부한 횟수만큼 증가하고 스트레스는 2 증가한다.
    Student_SetHP(stu, stu->hp -3);
    Student_SetIQ(stu, stu->iq + stu->scnt);
    Student_SetStress(stu, stu->stress + 2);
    stu->scnt = 0;
}
 
void Student_Relax(Student *stu)
{
    printf("<%d>:%s 류식하다. \n",stu->num, stu->name);
    //학생이 휴식하면 체력은 3회복하고 스트레스는 10 감소한다.
    Student_SetHP(stu, stu->hp +3);    
    Student_SetStress(stu, stu->stress -10);
    stu->scnt = 0;
}
 
void Student_Sleep(Student *stu)
{
    printf("<%d>:%s 잠을자다. \n",stu->num, stu->name);
    //학생이 잠을 자면 체력은 10 회복하고 스트레스는 5 감소한다. 
    Student_SetHP(stu, stu->hp + 10);    
    Student_SetStress(stu, stu->stress - 5);
    stu->scnt = 0;
}
 

void Student_Drink(Student *stu)
{
    printf("<%d>:%s 음료를 마시다. \n",stu->num, stu->name);
    //학생이 음료를 마시면 체력이 10 소모하며 지력은 5 감소하고 스트레스는 2 감소한다.
    Student_SetHP(stu, stu->hp -10);
    Student_SetIQ(stu, stu->iq - 5);
    Student_SetStress(stu, stu->stress - 2);
    stu->scnt = 0;
}
 
void Student_Sing(Student *stu)
{
    printf("<%d>:%s 노래하다. \n",stu->num, stu->name);
    // 학생이 노래하면 체력이 10 소모하며 지력은 1 증가하고 스트레스는 5 감소한다.
    Student_SetHP(stu, stu->hp -10);
    Student_SetIQ(stu, stu->iq + 1);
    Student_SetStress(stu, stu->stress - 5);
    stu->scnt = 0;
}
 
 
void Student_View(Student *stu)
{
    printf("<%d>:%s\n",stu->num, stu->name);
    printf("\t 체력:%d 아이큐:%d 스트레스:%d\n",stu->hp, stu->iq,stu->stress);
}
void Student_SetHP(Student *stu, int hp)
{
    if(hp > MAX_HP)
    {
        hp = MAX_HP;
    }
    if(hp < MIN_HP)
    {
        hp = MIN_HP;
    }
    stu->hp = hp;
}
void Student_SetIQ(Student *stu, int iq)
{
    if(iq > MAX_IQ)
    {
        iq = MAX_IQ;
    }
    if(iq < MIN_IQ)
    {
        iq = MIN_IQ;
    }
    stu->iq = iq;
}
void Student_SetStress(Student *stu, int stress)
{
    if(stress > MAX_STRESS)
    {
        stress = MAX_STRESS;
    }
    if(stress < MIN_STRESS)
    {
        stress = MIN_STRESS;
    }
    stu->stress = stress;
}
void Student_IncreSCnt(Student *stu)
{
    if(stu->scnt < MAX_SCNT)
    {
        stu->scnt++;
    }
}

80. 사용자 정의 형식 실습 – 테스트

먼저 Program.c 소스 파일을 추가하여 포함할 헤더 파일을 명시하세요.

#include "Student.h"
#include <stdio.h>

진입점 main 함수에서는 두 명의 학생을 테스트 하세요.

void TestStudent(Student *stu, const char *name);
int main()
{
    Student stu1;
    Student stu2;
    TestStudent(&stu1,"홍길동");
    TestStudent(&stu2,"강감찬");
    return 0;
}

학생을 테스트 하는 함수를 작성합시다.

void TestStudent(Student *stu, const char *name)
{
 테스트 시작을 콘솔 화면에 출력하세요.
    printf("--------------%s 학생 테스트 시작--------------\n",name);
 학생 생성자를 호출하여 초기 설정해야겠죠.
    Student_Student(stu,name);
 초기 설정이 정상적인지 확인하기 위해 학생 정보를 출력하세요.
    Student_View(stu);
 그리고 각 기능을 호출하여 정상적으로 동작하는지 확인하는 코드를 작성하세요.
 여기에서는 여러분께서 어떻게 작성하면 테스트가 가능한지 생각하면서 적절한 함수를 호출하세요.
    Student_Study(stu);
    Student_View(stu);
 ... 중략...
    printf("--------------%s 학생 테스트 종료--------------\n",name);
}

◈ Program.c

#include "Student.h"
#include <stdio.h>
void TestStudent(Student *stu, const char *name);
int main()
{
    Student stu1;
    Student stu2;
    TestStudent(&stu1,"홍길동");
    TestStudent(&stu2,"강감찬");
    return 0;
}
void TestStudent(Student *stu, const char *name)
{
    printf("--------------%s 학생 테스트 시작--------------\n",name);
    Student_Student(stu,name);
    Student_View(stu);
    Student_Study(stu);
    Student_View(stu);
    Student_Study(stu);
    Student_View(stu); 
    Student_ListenLecture(stu);
    Student_View(stu);
    Student_Study(stu);
    Student_View(stu);
    Student_Relax(stu);
    Student_View(stu);
    Student_Sleep(stu);
    Student_View(stu);
    Student_Drink(stu);
    Student_View(stu);
    Student_Sing(stu);
    Student_View(stu);    
    printf("--------------%s 학생 테스트 종료--------------\n",name);
}

◈ 실행 결과

--------------홍길동 학생 테스트 시작--------------
<1>:홍길동
              체력:100 아이큐:100 스트레스:0
<1>:홍길동 공부하다.
<1>:홍길동
              체력:95 아이큐:100 스트레스:0
<1>:홍길동 공부하다.
<1>:홍길동
              체력:90 아이큐:101 스트레스:0
<1>:홍길동 강의받다.
<1>:홍길동
              체력:87 아이큐:103 스트레스:2
<1>:홍길동 공부하다.
<1>:홍길동
              체력:82 아이큐:103 스트레스:0
<1>:홍길동 류식하다.
<1>:홍길동
              체력:85 아이큐:103 스트레스:0
<1>:홍길동 잠을자다.
<1>:홍길동
              체력:95 아이큐:103 스트레스:0
<1>:홍길동 음료를 마시다.
<1>:홍길동
              체력:85 아이큐:98 스트레스:0
<1>:홍길동 노래하다.
<1>:홍길동
              체력:75 아이큐:99 스트레스:0
--------------홍길동 학생 테스트 종료--------------
--------------강감찬 학생 테스트 시작--------------
<2>:강감찬
              체력:100 아이큐:100 스트레스:0
<2>:강감찬 공부하다.
<2>:강감찬
              체력:95 아이큐:100 스트레스:0
<2>:강감찬 공부하다.
<2>:강감찬
              체력:90 아이큐:101 스트레스:0
<2>:강감찬 강의받다.
<2>:강감찬
              체력:87 아이큐:103 스트레스:2
<2>:강감찬 공부하다.
<2>:강감찬
              체력:82 아이큐:103 스트레스:0
<2>:강감찬 류식하다.
<2>:강감찬
              체력:85 아이큐:103 스트레스:0
<2>:강감찬 잠을자다.
<2>:강감찬
              체력:95 아이큐:103 스트레스:0
<2>:강감찬 음료를 마시다.
<2>:강감찬
              체력:85 아이큐:98 스트레스:0
<2>:강감찬 노래하다.
<2>:강감찬
              체력:75 아이큐:99 스트레스:0
--------------강감찬 학생 테스트 종료--------------

81. 정리하기 (사용자 정의 형식 74~80)

1. 사용자 정의 형식 실습에서 사용했던 시나리오 규모의 시나리오를 작성하시오.

2. 작성한 시나리오로 형식을 정의하고 기능 구현 및 테스트를 하시오.