c언어 모듈화 프로그래밍 어떻게 하나요?(구조체, 상수, 전역변수 정리하기)

조회수 1059회

제가 만든 도서관리 프로그램입니다 ! 코드는 안보셔도 되고 아래에 글 한번만 읽어주세요 ㅠㅠ

#define _CRT_SECURE_NO_WARNIGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define BUFFER_SIZE 50
#define SMALL_BUFFER 20

struct library {
    int book_number; // 책 번호
    int check_borrowable; // 대여 가능하면 1, 불가능하면 0
    char title[BUFFER_SIZE]; // 책 제목
    char author[BUFFER_SIZE]; // 책 저자
    char publisher[BUFFER_SIZE]; // 책 출판사
};

void print_line();
void print_guide();
void print_information(struct library* book, int n);
void check_order(struct library* book, char* input_order); // 명령을 받아 대신 호출하는 함수
void add_book(struct library *book); // 책 추가
void search_number(struct library* book); // 검색 - 책 번호로
void search_title(struct library* book); // 검색 - 책 제목으로
void search_author(struct library* book); // 검색 - 책 저자명으로
void search_publisher(struct library* book); // 검색 - 책 출판사로
void borrow_book(struct library* book); // 책 대여 시 1 -> 0
void return_book(struct library* book); // 책 반납 시 0 -> 1

int total_books; // 등록된 책의 갯수

int main() {
    struct library book[100];
    char input_order[SMALL_BUFFER];

    print_guide();
    while (printf("\n$ ")) {
        scanf("%s", input_order);
        check_order(book, input_order);
    }

    return 0;
}

void print_line() {
    printf("\n-------------------------------\n");
}

void print_guide() {
    printf("\n--------- 명령어 안내 ---------\n");
    printf("1. 책 추가하기 : add\n");
    printf("2. 책 검색하기 : search\n");
    printf("2-1. 번호로 검색할 때 : + \"number\"\n");
    printf("2-2. 제목로 검색할 때 : + \"title\"\n");
    printf("2-3. 저자로 검색할 때 : + \"author\"\n");
    printf("2-4. 출판사로 검색할 때 : + \"piblisher\"\n");
    printf("3. 책 대여하기 : borrow\n");
    printf("4. 책 반납하기 : return\n");
    printf("5. 프로그램 종료 : exit\n");
    printf("\n");
}

void print_information(struct library* book, int n) {
    printf("\n찾으시는 책의 정보를 출력합니다.\n");
    printf(" 책 번호 : %d\n", book[n].book_number);
    printf(" 책 대여가능 여부 : ");
    if (book[n].check_borrowable == 1)
        printf("%s\n", "가능");
    else // book[n].check_borrowable == 0 
        printf("%s\n", "불가능");
    printf(" 책 제목 : %s\n", book[n].title);
    printf(" 책 저자 이름 : %s\n", book[n].author);
    printf(" 출판사 : %s\n", book[n].publisher);
    print_line();
}

void check_order(struct library* book, char* input_order) {
    if (strcmp(input_order, "add") == 0)
        add_book(book);
    else if (strcmp(input_order, "search") == 0) {
        char input[SMALL_BUFFER];
        scanf("%s", input);
        if (strcmp(input, "number") == 0)
            search_number(book);
        else if (strcmp(input, "title") == 0)
            search_title(book);
        else if (strcmp(input, "author") == 0)
            search_author(book);
        else if (strcmp(input, "publisher") == 0)
            search_publisher(book);
        else
            printf("유효하지 않은 명령입니다.\n");
    }
    else if (strcmp(input_order, "borrow") == 0)
        borrow_book(book);
    else if (strcmp(input_order, "return") == 0)
        return_book(book);
    else if (strcmp(input_order, "exit") == 0) {
        printf("프로그램을 종료합니다.\n");
        exit(0);
    }
    else
        printf("유효하지 않은 명령입니다.\n");
}

void add_book(struct library *book) {
    book[total_books].book_number = total_books + 1;
    book[total_books].check_borrowable = 1;
    print_line();
    printf("책 %d에 대한 정보를 입력하십시오.\n", book[total_books].book_number);
    printf("책 제목 : ");
    scanf("%s", book[total_books].title);
    printf("책 저자 : ");
    scanf("%s", book[total_books].author);
    printf("출판사 : ");
    scanf("%s", book[total_books].publisher);
    printf(">>성공적으로 추가되었습니다.\n");
    total_books++;
}

void search_number(struct library* book) {
    int find_number;
    printf("찾으실 책의 번호를 입력하세요 : ");
    scanf("%d", &find_number);
    if (find_number < 1 && find_number > total_books)
        printf("유효하지 않은 값입니다.\n");
    else if (book[find_number - 1].check_borrowable == 0)
        printf("이미 대여중인 책이므로 찾을 수 없습니다.\n");
    else
        print_information(book, find_number - 1);
}

void search_title(struct library* book) {
    char input[SMALL_BUFFER];
    int check_num = 0;
    printf("찾으실 책의 이름을 입력하세요 : ");
    scanf("%s", input);
    /* 등록되어 있는 모든 책을 대상으로 검사함 */
    for (int i = 0; i < total_books; i++) {
        if (book[i].check_borrowable == 0) // 이미 대여중이라면 정보 출력 X
            continue;
        else {
            /* 등록된 책 순서대로 이름의 첫 번째 알파벳부터 null character 전까지 검사함 */
            for (int j = 0; j < strlen(book[i].title); j++) {
                /* strncmp -> 처음 써보는 함수인데 ,, 
                 세 인자 중 첫 번째와 두 번째 인자를 비교하는데 앞에서부터 strlen(input) 만큼만 검사하라는 함수 

                 또한 if문을 포함한 다른 함수도 0이면 false 이고 그 외의 것들은 모두 true이다.
                 strncmp 함수는 동일할 때 0을 반환하기 때문에 ! 연산자를 붙이면 1이 된다.
                 그러므로 두 문자열이 조건에 맞게 동일하다면 1을 반환하고, if문 조건이 참이 되므로 내부 식을 실행한다.*/
                if (!strncmp(book[i].title + j, input, strlen(input))) {
                    print_information(book, i);
                    check_num++;
                }
            }
        }
    }
    if (check_num == 0)
        printf("\"%s\" 라는 이름의 책은 존재하지 않습니다.\n", input);
    else
        printf("\n검색된 책은 총 %d권 입니다.\n", check_num);
}

void search_author(struct library* book) {
    char input[SMALL_BUFFER];
    int check_num = 0;
    printf("찾으실 책의 저자 이름을 입력하세요 : ");
    scanf("%s", input);
    for (int i = 0; i < total_books; i++) {
        if (strcmp(input, book[i].author) == 0) {
            if (book[i].check_borrowable == 0) // 이미 대여중이라면 정보 출력 X
                continue;
            else {
                print_information(book, i);
                check_num++;
            }
        }
    }
    if (check_num == 0)
        printf("\"%s\" 라는 저자의 책은 존재하지 않습니다.\n", input);
    else
        printf("\n검색된 책은 총 %d권 입니다.\n", check_num);
}

void search_publisher(struct library* book) {
    char input[SMALL_BUFFER];
    int check_num = 0;
    printf("찾으실 책의 출판사를 입력하세요 : ");
    scanf("%s", input);
    for (int i = 0; i < total_books; i++) {
        if (strcmp(input, book[i].publisher) == 0) {
            if (book[i].check_borrowable == 0) // 이미 대여중이라면 정보 출력 X
                continue;
            else {
                print_information(book, i);
                check_num++;
            }
        }
    }
    if (check_num == 0)
        printf("\"%s\" 라는 출판사의 책은 존재하지 않습니다.\n", input);
    else
        printf("\n검색된 책은 총 %d권 입니다.\n", check_num);
}

void borrow_book(struct library* book) {
    int borrow_number;
    printf("빌릴 책의 번호를 입력하세요 : ");
    scanf("%d", &borrow_number);
    if (borrow_number < 1 && borrow_number > total_books)
        printf("%d번 책은 존재하지 않습니다.\n", borrow_number);
    else if (book[borrow_number - 1].check_borrowable == 0)
        printf("이미 대여중인 책입니다.\n");
    else {
        book[borrow_number - 1].check_borrowable = 0;
        printf("성공적으로 대여 처리가 완료되었습니다.\n");
    }
}

void return_book(struct library* book) {
    int borrow_number;
    printf("반납할 책의 번호를 입력하세요 : ");
    scanf("%d", &borrow_number);
    if (borrow_number < 1 && borrow_number > total_books)
        printf("%d번 책은 존재하지 않습니다.\n", borrow_number);
    else {
        book[borrow_number - 1].check_borrowable = 1;
        printf("성공적으로 반납 처리가 완료되었습니다.\n");
    }
}

지금까지는 그냥 닥치는데로 한 개의 소스 파일에 모든 코드를 작성했었는데 "모듈화 프로그래밍"이라는 것을 알게 되면서 함수의 선언부분 (아래)

void print_line();
void print_guide();
void print_information(struct library* book, int n);
void check_order(struct library* book, char* input_order); // 명령을 받아 대신 호출하는 함수
void add_book(struct library *book); // 책 추가
void search_number(struct library* book); // 검색 - 책 번호로
void search_title(struct library* book); // 검색 - 책 제목으로
void search_author(struct library* book); // 검색 - 책 저자명으로
void search_publisher(struct library* book); // 검색 - 책 출판사로
void borrow_book(struct library* book); // 책 대여 시 1 -> 0
void return_book(struct library* book); // 책 반납 시 0 -> 1

새로운 헤더파일( new_func.h ) 를 만들어서 그 곳에 선언 후 #include"new_func.h" 를 하고, main 함수를 제외한 모든 함수의 정의 부분은 새로운 소스 파일 ( new_func.c )를 만들어서 그 곳에 정의를 하려고 합니다. 그런데 여기서 문제가 생기네요. main함수가 포함된 소스파일에 선언된 상수, 구조체, 전역변수 는 새로 만든 소스파일에서도 필요한 정보잖아요. 이를 어떻게 넘겨줄지 모르겠네요... 정말 오랜시간 다양한 방법으로 시도해보고 찾아봤는데도 잘 모르겠어서 이렇게 질문을 남깁니다. 전역변수는 extern 을 써서 선언하면 된다고 하던데 사용 안하는게 다들 좋다고해서 ,, 여튼 글이 길어졌습니다. 고수분들 답변 기다리겠습니다. 긴 글 읽어주셔서 감사합니다 !!

  • (•́ ✖ •̀)
    알 수 없는 사용자

1 답변

    1. struct librarylibrary.h 같은 헤더파일을 하나 더 만들어서 사용되는 곳에서 include 해서 쓰면 될 것 같고요.
    2. 전역변수 total_books 는 저렇게 전역변수로 놓으면 정말 안좋고, 저라면, struct shelve 같은 스트럭처 하나 만들어서 그 안에 library (의미상으론 book 이 더 적합하고) 배열이랑, 저장된 책의 갯수랑 같이 저장해서 관리하겠습니다.
    • 오,, 혼자 끙끙대다 결국 나누는거 성공했는데 답변 달아주셨군요 ! 한번 다시 시도해보겠습니다 ! 도움 주셔서 정말 감사합니다 !! :) 알 수 없는 사용자 2020.2.5 19:37
    • 혹여나 다시 이 글을 보시게 된다면 하나만 더 답변 해주실수 있으실까요??ㅠㅠ 혹시 total_books를 전역변수로 선언할 시 왜 안좋은지 이유를 알 수 있을까요?? 알 수 없는 사용자 2020.2.5 20:03
    • 전역변수는 "다" 안 좋아요. nowp 2020.2.5 20:28
    • ㅠㅠㅠ 간단한 이유나 참고할만한 글 없을까용 알 수 없는 사용자 2020.2.5 20:29
    • 무조건 나쁘것은 없습니다. 장단점을 알고 적재적소에 사용하면 되는 겁니다. 전역변수의 경우 관리가 어렵다는 단점이 있는 겁니다. 어디서 변경이 될지 알 수 없으므로 어떤 함수에서의 전역 변수 값이 얼마일지 파악하기가 어렵습니다. 이는 코드 유지보수가 나쁘게 되는 이유가 됩니다. 즉 남발하면 유지보수하기 어려운 코드가 되겠지요. 전역변수를 피하겠다고 복잡한 설계를 가져갈바에 간단하게 전역변수로 깔끔하게 처리할 수도 있는 거지요. 정영훈 2020.2.5 21:14
    • 고인물께서 좋은 답변 주셨네요. 맞는 말씀이시니, 참고하시고. 처음 배우는 입장에서는 전역변수는 절대 쓰지 말아야지, goto 는 절대 쓰지 말아야지 하는 마음가짐으로 습관을 들여야 더 좋은 코드를 쓸 수가 있어요. nowp 2020.2.6 12:09

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)