9장 구조체 vs. 클래스

감사의 글

유튜브 동영상 모음집 (나도코딩 C)에서 설명하는 코드를 활용합니다. 강의동영상을 공개한 나도코딩님께 감사드립니다.

핵심

  • C 언어의 구조체 이해
  • C 언어의 구조체와 파이썬의 클래스 비교

(C 언어) 본격 고양이 수집게임

게임 설명

  • 고양이의 특성을 보여주는 앱 개발 프로젝트
    • 이름, 나이, 특징, 레벨

(C 언어) 구조체 기본


#include <stdio.h>

int main()
{
    // [게임 출시 1]
    // 이름: 나도게임
    // 발매년도: 2017년
    // 가격: 50원
    // 제작사: 나도회사    

    char * name = "나도게임";
    int year = 2017;
    int price = 50;
    char * company = "나도회사";

    // [게임 출시 2]
    // 이름: 너도게임
    // 발매년도: 2020년
    // 가격: 100원
    // 제작사: 너도회사    

    char * name2 = "너도게임";
    int year2 = 2017;
    int price2 = 100;
    char * company2 = "너도회사";

   return 0;
}

(C 언어) 구조체 사용

  • 동영상: (나도코딩 C) 9-3 구조체 사용

  • 게임출시가 계속해서 이어지면? 비슷한 변수를 계속해서 반복적으로 복사해야 함. 하나의 게임과 관련된 자료를 함께 관리하기도 어려움. 이를 위해 구조체 활용 가능


#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
struct GameInfo {
    char * name;
    int year;
    int price;
    char * company;
};

int main()
{
    // 게임 출시 1
    struct GameInfo gameinfo1;

    gameinfo1.name = "나도게임";
    gameinfo1.year = 2017;
    gameinfo1.price = 50;
    gameinfo1.company = "나도회사";

    // 구조체 항목 출력
    printf("-- 게임출시 1 정보 --\n");
    printf("  게임명    : %s\n", gameinfo1.name);
    printf("  발매년도  : %d\n", gameinfo1.year);
    printf("  가격      : %d\n", gameinfo1.price);
    printf("  제작사    : %s\n", gameinfo1.company);

    return 0;
}


#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
struct GameInfo {
    char * name;
    int year;
    int price;
    char * company;
};

int main()
{
    // 게임 출시 1
    struct GameInfo gameinfo1 = {"나도게임", 2017, 50, "나도회사"};

    // 게임 출시 2
    struct GameInfo gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    // 구조체 항목 출력
    printf("-- 게임출시 1 정보 --\n");
    printf("  게임명    : %s\n", gameinfo1.name);
    printf("  발매년도  : %d\n", gameinfo1.year);
    printf("  가격      : %d\n", gameinfo1.price);
    printf("  제작사    : %s\n", gameinfo1.company);

    printf("\n\n-- 게임출시 2 정보 --\n");
    printf("  게임명    : %s\n", gameinfo2.name);
    printf("  발매년도  : %d\n", gameinfo2.year);
    printf("  가격      : %d\n", gameinfo2.price);
    printf("  제작사    : %s\n", gameinfo2.company);

    return 0;
}

Python 구현

  • 파이썬은 구조체를 지원하지 않음.
  • 하지만 구조체를 클래스로 다룰 수 있음.
In [1]:
# 클래스 선언

# 게임 출시: 이름, 발매년도, 가격, 제작사
class GameInfo:
    def __init__(self, name:str, year:int, price:int, company:str):
        self.name = name
        self.year = year
        self.price = price
        self.company = company
    

# 클래스의 인스턴스 생성    

# 게임 출시 1
gameinfo1 = GameInfo("나도게임", 2017, 50, "나도회사")

# 게임 출시 2
gameinfo2 = GameInfo("너도게임", 2020, 100, "너도회사")

# 구조체 항목 출력
print("-- 게임출시 1 정보 --")
print("  게임명   : %s" % gameinfo1.name)
print("  발매년도  : %d" % gameinfo1.year)
print("  가격      : %d" % gameinfo1.price)
print("  제작사    : %s"% gameinfo1.company)

print("\n-- 게임출시 2 정보 --")
print("  게임명    : %s" % gameinfo2.name)
print("  발매년도  : %d" % gameinfo2.year)
print("  가격      : %d" % gameinfo2.price)
print("  제작사    : %s" % gameinfo2.company)
-- 게임출시 1 정보 --
  게임명   : 나도게임
  발매년도  : 2017
  가격      : 50
  제작사    : 나도회사

-- 게임출시 2 정보 --
  게임명    : 너도게임
  발매년도  : 2020
  가격      : 100
  제작사    : 너도회사

(C 언어) 구조체 배열


#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
struct GameInfo {
    char * name;
    int year;
    int price;
    char * company;
};

int main()
{
    // 구조체로 이루어진 배열
    struct GameInfo gameArray[2] = {
        {"나도게임", 2017, 50, "나도회사"},
        {"너도게임", 2020, 100, "너도회사"}
    };

    return 0;
}

  • 또는

#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
struct GameInfo {
    char * name;
    int year;
    int price;
    char * company;
};

int main()
{
    struct GameInfo gameinfo1 = {"나도게임", 2017, 50, "나도회사"};
    struct GameInfo gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    // 구조체로 이루어진 배열
    struct GameInfo gameArray[2] = {
        gameinfo1,
        gameinfo2
    };

    return 0;
}

Python 구현

  • 리스트는 클래스를 항목으로 다룰 수 있음.
  • 실제로 파이썬의 리스트는 어떤 자료형도 항목으로 다룰 수 있음.
  • 이유: 파이썬의 모든 값은 일급 객체(first-class object)이기 때문임.
In [2]:
# 클래스 선언

# 게임 출시: 이름, 발매년도, 가격, 제작사
class GameInfo:
    def __init__(self, name:str, year:int, price:int, company:str):
        self.name = name
        self.year = year
        self.price = price
        self.company = company
    

# 클래스의 인스턴스 생성    

# 게임 출시 1
gameinfo1 = GameInfo("나도게임", 2017, 50, "나도회사")

# 게임 출시 2
gameinfo2 = GameInfo("너도게임", 2020, 100, "너도회사")

# 구조체 이루어진 리스트
gameArray = [gameinfo1, gameinfo2]

(C 언어) 구조체 포인터 변수

  • 동영상: (나도코딩 C) 9-5 구조체 포인터

  • 구조체가 저장된 위치를 가리키는 포인터 변수를 선언과 사용법 일반 포인터 변수의 경우와 동일.

    • 단. 괄호 사용에 주의할 것.

#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
struct GameInfo {
    char * name;
    int year;
    int price;
    char * company;
};

int main()
{
    // 게임출시 1
    struct GameInfo gameinfo1 = {"나도게임", 2017, 50, "나도회사"};
    // 게임출시 2
    struct GameInfo gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    // 구조체 포인터

    // 미션맨 구현
    struct GameInfo * gamePtr; 
    gamePtr = &gameinfo1;

    // 괄호사용에 주의
    printf("\n\n-- 미션맨게임 출시정보 --\n");
    printf("  게임명    : %s\n", (*gamePtr).name);
    printf("  발매년도  : %d\n", (*gamePtr).year);
    printf("  가격      : %d\n", (*gamePtr).price);
    printf("  제작사    : %s\n", (*gamePtr).company);

    return 0;
}

  • 구조체 포인터 변수는 화살표 기호(->)와 함께 사용하는 것이 편함.

#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
struct GameInfo {
    char * name;
    int year;
    int price;
    char * company;
};

int main()
{
    // 게임출시 1
    struct GameInfo gameinfo1 = {"나도게임", 2017, 50, "나도회사"};
    // 게임출시 2
    struct GameInfo gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    // 구조체 포인터

    // 미션맨 구현
    struct GameInfo * gamePtr; 
    gamePtr = &gameinfo1;

    // 화살표 기호(->) 사용하는 게 편함
    // 단, 별표 기호 사용하지 않음.
    printf("\n\n-- 미션맨게임 출시정보 --\n");
    printf("  게임명    : %s\n", gamePtr->name);
    printf("  발매년도  : %d\n", gamePtr->year);
    printf("  가격      : %d\n", gamePtr->price);
    printf("  제작사    : %s\n", gamePtr->company);

    return 0;
}

Python 구현

  • 해당사항 없음

(C 언어) 구조체 안의 구조체

  • 동영상: (나도코딩 C) 9-6 구조체 안의 구조체

  • 어레이 변수는 기본적으로 포인터 변수임을 7장 포인터에서 배웠음. 구조체를 가리키는 변수 역시 기본적으로 포인터 변수로 취급됨. 이 성질을 이용하여 구조체 안에 구조체를 가리키는 변수 사용 가능.


#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
struct GameInfo {
    char * name;
    int year;
    int price;
    char * company;
    struct GameInfo * friendGame;  // 연관업체 게임 정보
};

int main()
{
    // 게임출시
    struct GameInfo gameinfo1 = {"나도게임", 2017, 50, "나도회사"};
    // 연관업체 게임출시
    struct GameInfo gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    // 구조체 안의 구조체 포인터
    // 연관업체 게임 소개 추가
    gameinfo1.friendGame = &gameinfo2;

    printf("\n\n-- 연관업체의 게임 출시정보 --\n");
    printf("  게임명    : %s\n", gameinfo1.friendGame->name);
    printf("  발매년도  : %d\n", gameinfo1.friendGame->year);
    printf("  가격      : %d\n", gameinfo1.friendGame->price);
    printf("  제작사    : %s\n", gameinfo1.friendGame->company);

    return 0;
}

Python 구현

  • 파이썬 클래스의 속성으로 임의의 값이 사용될 수 있음. 따라서 임의의 클래스의 인스턴스 또한 속성의 값으로 지정될 수 있음.
In [3]:
# 클래스 선언

# 게임 출시: 이름, 발매년도, 가격, 제작사, 연관업체 게임 정보
# 클래스의 재귀적 활용에 주의할 것 (자료형 명시 기능에 유의!)

class GameInfo:
    def __init__(self, name:str, year:int, price:int, company:str, friendGame:GameInfo=None):
        self.name = name
        self.year = year
        self.price = price
        self.company = company
        self.friendGame = friendGame   # 연관업체 게임 정보
    
# 클래스의 인스턴스 생성    

# 게임 출시 1
gameinfo1 = GameInfo("나도게임", 2017, 50, "나도회사")

# 게임 출시 2
gameinfo2 = GameInfo("너도게임", 2020, 100, "너도회사")

# 연관업체 게임 소개 추가
gameinfo1.friendGame = gameinfo2

print("-- 연관업체의 게임 출시정보 --")
print("  게임명    : %s" % gameinfo1.friendGame.name)
print("  발매년도  : %d" % gameinfo1.friendGame.year)
print("  가격      : %d" % gameinfo1.friendGame.price)
print("  제작사    : %s" % gameinfo1.friendGame.company)
-- 연관업체의 게임 출시정보 --
  게임명    : 너도게임
  발매년도  : 2020
  가격      : 100
  제작사    : 너도회사

(C 언어) typedef 활용

  • 동영상: (나도코딩 C) 9-7 typedef

  • typedef 지정자: 이미 선언된 자료형의 별명짓기 용도로 사용됨

    typedef int 정수;
      typedef float 실수;
    
      정수 i = 3;    // int i = 3;
      실수 f = 3.23f // float f = 3.23f;
    
  • 구조체를 선언하면서 동시에 typedef 활용 가능


#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
// 자료형 선언하면서 별명을 동시에 지정
typedef struct GameInformation {
    char * name;
    int year;
    int price;
    char * company;
    struct GameInformation * friendGame;
} GAME_INFO;  // 별명

int main()
{
    // 구조체 별명 직접 활용
    GAME_INFO gameinfo1 = {"나도게임", 2017, 50, "나도회사"};
    GAME_INFO gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    gameinfo1.friendGame = &gameinfo2;

    printf("\n\n-- 연관업체의 게임 출시정보 --\n");
    printf("  게임명    : %s\n", gameinfo1.friendGame->name);
    printf("  발매년도  : %d\n", gameinfo1.friendGame->year);
    printf("  가격      : %d\n", gameinfo1.friendGame->price);
    printf("  제작사    : %s\n", gameinfo1.friendGame->company);

    return 0;
}

  • 별명을 사용하지 않아도 됨.

#include <stdio.h>

// 게임 출시: 이름, 발매년도, 가격, 제작사
typedef struct GameInformation {
    char * name;
    int year;
    int price;
    char * company;
    struct GameInformation * friendGame;
} GAME_INFO;  // 별명

int main()
{
    // 별명 사용하지 않아도 됨.
    struct GameInformation gameinfo1 = {"나도게임", 2017, 50, "나도회사"};
    GAME_INFO gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    gameinfo1.friendGame = & gameinfo2;

    printf("\n\n-- 연관업체의 게임 출시정보 --\n");
    printf("  게임명    : %s\n", gameinfo1.friendGame->name);
    printf("  발매년도  : %d\n", gameinfo1.friendGame->year);
    printf("  가격      : %d\n", gameinfo1.friendGame->price);
    printf("  제작사    : %s\n", gameinfo1.friendGame->company);

    return 0;
}

  • 주의사항: typedef 지정자를 사용할 때 구조체의 이름을 지정하지 않아도 되지만 재귀적인 구조체 정의는 불가능함. 즉, 아래 경우는 선언 불가능

    typedef struct {
      char * name;
      int year;
      int price;
      char * company;
      struct GAME_INFO * friendGame;  // 연관업체 게임 정보
      } GAME_INFO;
    
  • 하지만 아래 경우는 물론 가능.

#include <stdio.h>

struct GameInfo{
    char * name;
    int year;
    int price;
    char * company;
    struct GameInfo * friendGame;  // 연관업체 게임 정보
};

// 별명만 사용. 재귀적으로 사용되지는 않았음.
typedef struct {
    char * name;
    int year;
    int price;
    char * company;
    struct GameInfo * friendGame;  // 연관업체 게임 정보
} GAME_INFO;

int main()
{
    // 게임출시
    GAME_INFO gameinfo1 = {"나도게임", 2017, 50, "나도회사"};
    // 연관업체 게임출시
    struct GameInfo gameinfo2 = {"너도게임", 2020, 100, "너도회사"};

    // 구조체 안의 구조체 포인터
    // 연관업체 게임 소개 추가
    gameinfo1.friendGame = & gameinfo2;

    printf("\n\n-- 연관업체의 게임 출시정보 --\n");
    printf("  게임명    : %s\n", gameinfo1.friendGame->name);
    printf("  발매년도  : %d\n", gameinfo1.friendGame->year);
    printf("  가격      : %d\n", gameinfo1.friendGame->price);
    printf("  제작사    : %s\n", gameinfo1.friendGame->company);

    return 0;
}

Python 구현

  • 해당사항 없음
  • 파이썬 클래스를 선언할 때 반드시 이름을 사용해야 하며, 클래스 이름이 자료형으로 작동함.

(C 언어) 프로젝트


#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 5 마리의 고양이가 있음.
// 아무 키나 눌러서 랜덤으로 고양이를 뽑아 5 마리를 다 모아 키우면 됨.
// 중복 발생 가능

// 고양이 구조체 선언
typedef struct {
    char * name;      // 이름
    int age;          // 나이
    char * character; // 성격
    int level;        // 키우기 난이도 (1-5, 5가 어려움)
} CAT;

// 현재까지 보유한 고양이 목록
int collection[5] = { 0, 0, 0, 0, 0 };

// 전체 고양이 목록 (구조체 리스트)
CAT cats[5];

void initCats(); // 고양이 정보 초기화
void printCat(int selected);
int checkCollection();

int main()
{
    srand(time(NULL));

    // 기본 세팅
    initCats();

    while (1)
    {
        // 임의 키 입력할 때 고양이 한 마리 무작위 선택
        printf("두근두근~! 어느 고양이의 집사가 될까요?\n아무 키나 눌러서 확인하세요!");
        getchar();  // 임의 키 입력 대기

        int selected = rand() % 5; // 0-4 사이의 숫자 반환

        // 뽑힌 고양이 정보 출력
        printCat(selected);

        // 선택된 고양이 표시하기
        collection[selected] = 1;

        // 5 마리 모두 선택되었는지 여부 확인
        int collectAll = checkCollection();

        // 모두 선택한 경우 종료
        if (collectAll)
        {
            break;
        }
    }

    return 0;
}

void initCats()
{
    cats[0].name = "깜냥이";
    cats[0].age = 5;
    cats[0].character = "온순";
    cats[0].level = 1;

    cats[1].name = "귀요미";
    cats[1].age = 3;
    cats[1].character = "날카로움";
    cats[1].level = 2;

    cats[2].name = "수줍이";
    cats[2].age = 7;
    cats[2].character = "늘 잔만 잠";
    cats[2].level = 3;

    cats[3].name = "깍꿍이";
    cats[3].age = 2;
    cats[3].character = "시끄러움";
    cats[3].level = 4;

    cats[4].name = "돼냥이";
    cats[4].age = 1;
    cats[4].character = "배고파함";
    cats[4].level = 5;
}

void printCat(int selected)
{
    printf("\n\n=== 당신은 이 공양이의 집사가 되었어요! ===\n\n");
    printf(" 이름:      %s\n", cats[selected].name);
    printf(" 나이:      %d\n", cats[selected].age);
    printf(" 특징(성격): %s\n", cats[selected].character);
    printf(" 레벨: ");

    for (int i = 0 ; i < cats[selected].level; i++)
    {
        printf("*");
    }
    printf("\n\n");
}

// 5 마리 고양이 모두 선택하였는지 여부 확인
int checkCollection()
{
    // 1. 현재 보유한 고양이 목록 출력하면서
    // 2. 동시에 다 모았는지 여부를 반환

    int collectAll = 1; 

    printf("\n\n === 보유한 고양이 목록입니다 === \n\n");
    for (int i = 0 ; i < 5; i++)
    {
        if (collection[i] == 0) // 고양이 수집 X
        {
            printf("%10s", "(빈 박스)");
            collectAll = 0;
        }
        else // 고양이 수집 O
        {
            printf("%10s", cats[i].name);
        }
    }

    printf("\n============\n\n");

    if (collectAll) // 모든 고양이를 수집한 경우
        {
            printf("\n\n축하합니다! 모든 고양이를 다 모았어요. 열심히 키워주세요.\n\n");
        }

    return collectAll;
}

Python 구현

In [ ]:
import random 
import time
import sys
 
current_time = int(time.time())
random.seed(current_time)

class CAT():
    def __init__(self, name, age, character, level):
        self.name = name
        self.age = age
        self.character = character
        self.level = level

collection = [ 0, 0, 0, 0, 0 ]

cats = [None] * 5

def initCats():
    cats[0] = CAT("깜냥이", 5, "온순", 1)
    cats[1] = CAT("귀요미", 3, "날카로움", 2)
    cats[2] = CAT("수줍이", 7, "늘 잠만 잠", 3)
    cats[3] = CAT("깍꿍이", 2, "시끄러움", 4)
    cats[4] = CAT("돼냥이", 1, "배고파함", 5)

def printCat(selected):
    print("\n=== 당신은 이 공양이의 집사가 되었어요! ===\n")
    print(" 이름:      %s" % cats[selected].name)
    print(" 나이:      %d" % cats[selected].age)
    print(" 특징(성격): %s" % cats[selected].character)
    print(" 레벨: ", end='')

    for i in range(cats[selected].level):
        print("*", end='')

    print("\n")


def checkCollection():
    collectAll = 1

    print("\n\n === 보유한 고양이 목록입니다 === \n")
    for i in range(5):
        if (collection[i] == 0):
            print("%10s" % "(빈 박스)", end='')
            collectAll = 0
        else:
            print("%10s" % cats[i].name, end='')

    print("\n============\n")

    if (collectAll):
            print("\n\n축하합니다! 모든 고양이를 다 모았어요. 열심히 키워주세요.\n")

    return collectAll


if __name__=="__main__":

    initCats()

    while True:
        print("두근두근~! 어느 고양이의 집사가 될까요?\n아무 키나 눌러서 확인하세요!")

        input()

        selected = random.randint(0, sys.maxsize)%5
        printCat(selected)

        collection[selected] = 1

        collectAll = checkCollection()

        if (collectAll):
                break

코드 개선 1: 인스턴스 메서드 활용

  • 위에서 정의된 CAT 클래스는 아무런 메서드를 갖고 있지 않음.
  • 하지만 printCat() 함수를 인스턴스 객체 자신을 소개하는 하나의 메서드로 정의할 수 있음.
    • 그러면 printCat(selected)가 필요할 때마다 선택된 고양이의 printCat() 메서드를 호출하면 됨.
In [ ]:
import random 
import time
import sys
 
current_time = int(time.time())
random.seed(current_time)

# 고양이 클래스
class CAT():
    def __init__(self, name, age, character, level):
        self.name = name
        self.age = age
        self.character = character
        self.level = level

    # 고양이 자신 정보 출력
    def printCat(self):
        print("\n=== 당신은 이 공양이의 집사가 되었어요! ===\n")
        print(" 이름:      %s" % self.name)
        print(" 나이:      %d" % self.age)
        print(" 특징(성격): %s" % self.character)
        print(" 레벨: ", end='')

        for i in range(self.level):
            print("*", end='')

        print("\n")

# 게임 세팅        
collection = [ 0, 0, 0, 0, 0 ]
cats = [None] * 5

def initCats():
    cats[0] = CAT("깜냥이", 5, "온순", 1)
    cats[1] = CAT("귀요미", 3, "날카로움", 2)
    cats[2] = CAT("수줍이", 7, "늘 잠만 잠", 3)
    cats[3] = CAT("깍꿍이", 2, "시끄러움", 4)
    cats[4] = CAT("돼냥이", 1, "배고파함", 5)


def checkCollection():
    collectAll = 1

    print("\n\n === 보유한 고양이 목록입니다 === \n")
    for i in range(5):
        if (collection[i] == 0):
            print("%10s" % "(빈 박스)", end='')
            collectAll = 0
        else:
            print("%10s" % cats[i].name, end='')

    print("\n============\n")

    if (collectAll):
            print("\n\n축하합니다! 모든 고양이를 다 모았어요. 열심히 키워주세요.\n")

    return collectAll

# 게임 시작
if __name__=="__main__":

    # 5마리 고양이 준비하기
    initCats()

    # 고양이 선택 반복
    while True:
        print("두근두근~! 어느 고양이의 집사가 될까요?\n아무 키나 눌러서 확인하세요!")

        input()

        selected = random.randint(0, sys.maxsize)%5
        cats[selected].printCat()

        collection[selected] = 1

        collectAll = checkCollection()

        if (collectAll):
                break

코드 개선 2: 전역변수를 클래스 속성으로 활용하기

  • 앞서 CAT 클래스를 선언하여서 잘 활용하였음.
  • 그리고 collection, cats 두 변수는 게임 세팅을 위해 필요한 변수임.
    • 그런데 두 변수가 initCats() 함수와 checkCollection() 함수에서 전역변수로 사용됨.
  • 이런 경우처럼 전역변수를 사용하는 함수와 사용되는 전역변수를 하나의 클래스로 묶어서 사용하는 게 보다 안정성이 보장되는 프로그램를 작성할 수 있음.
    • 전역변수는 누구가 사용하고 수정할 수 있기 때문에 보안문제 등이 발생할 수 있음.
  • 여기서는 게임세팅을 위한 클래스의 속성변수와 메서드로 선언할 수 있음.
In [ ]:
import random 
import time
import sys
 
current_time = int(time.time())
random.seed(current_time)

# 고양이 클래스 
class CAT():
    def __init__(self, name, age, character, level):
        self.name = name
        self.age = age
        self.character = character
        self.level = level
    
    # 고양이 자신 정보 출력
    def printCat(self):
        print("\n=== 당신은 이 공양이의 집사가 되었어요! ===\n")
        print(" 이름:      %s" % self.name)
        print(" 나이:      %d" % self.age)
        print(" 특징(성격): %s" % self.character)
        print(" 레벨: ", end='')

        for i in range(self.level):
            print("*", end='')

        print("\n")

# 게임 세팅 클래스        
class CollectedCats():
    # 인스턴스를 생성할 때 고양이 수만큼의 이름, 나이, 특징, 레벨을 각각 리스트로 받음.
    # collection, cats 변수가 인스턴스 변수로 선언됨.
    def __init__(self, names, ages, characters, levels):
        self.numCats = len(names)                                # 고양이 수

        self.collection = [0] * self.numCats
    
        # 고양이 목록 초기화
        self.cats = [None] * self.numCats
        for i in range(self.numCats):
            self.cats[i] = CAT(names[i], ages[i], characters[i], levels[i])

    def checkCollection(self):
        collectAll = 1

        print("\n\n === 보유한 고양이 목록입니다 === \n")
        for i in range(self.numCats):
            if (self.collection[i] == 0):
                print("%10s" % "(빈 박스)", end='')
                collectAll = 0
            else:
                print("%10s" % self.cats[i].name, end='')

        print("\n============\n")

        if (collectAll):
                print("\n축하합니다! 모든 고양이를 다 모았어요. 열심히 키워주세요.\n")

        return collectAll

# 게임 시작
if __name__=="__main__":

    # 5마리 고양이 준비하기
    # 5마리 고양이의 이름, 나이, 특징, 레벨 각 항목에 대한 리스트로 지정
    names = ["깜냥이", "귀요미", "수줍이", "깍꿍이", "돼냥이"]
    ages = [5, 3, 7, 2, 1]
    characters = ["온순", "날카로움", "늘 잠만 잠", "시끄러움", "배고파함"]
    levels = [1, 2, 3, 4, 5]
    
    ourCats = CollectedCats(names, ages, characters, levels)

    # 고양이 선택 반복
    while True:
        print("두근두근~! 어느 고양이의 집사가 될까요?\n아무 키나 눌러서 확인하세요!")

        input()

        selected = random.randint(0, sys.maxsize)%5
        ourCats.cats[selected].printCat()

        ourCats.collection[selected] = 1

        collectAll = ourCats.checkCollection()

        if (collectAll):
                break

부록: (C 언어) 구조체 포인터

  • 구조체를 선언한 후에 해당 구조체의 자료형 값을 선언할 때 두 가지 방식이 있음.

  • 예제: 포인터로 구성된 구조체를 이용하여 예를 들어 자료형이 다른 배열을 구조체로 하나로 묶기

방식 1

  • 구조체를 직접 선언할 수 있음.

#include <stdio.h>

int main()
{
    // 자료형이 다른 두 개의 배열
    int arr1[2] = {10, 11};
    float arr2[3] = {1.0, 1.1, 1.2};

    // 구조체 자료형 선언
    typedef struct
    {
        int * intArray;
        float * floatArray;
    } mixedArray;

    // 구조체 직접 정의
    mixedArray arrMixed = {arr1, arr2};

    // 활용
    printf("%d\n", arrMixed.intArray[0]);
    printf("%.1f\n", arrMixed.floatArray[2]);

    return 0;
}

방식 2: 메모리 동적 할당

  • 구조체 포인터를 활용할 수 있음.
  • 하지만 malloc() 함수를 이용하여 동적으로 메모리 할당(dynamic memory allocation)을 먼저 해야 구조체를 할당할 수 있음.

#include <stdio.h>
#include <stdlib.h>  // 추가 라이브러리 필요

int main()
{
    // 자료형이 다른 두 개의 배열
    int arr1[2] = {10, 11};
    float arr2[3] = {1.0, 1.1, 1.2};

    // 구조체 자료형 선언
    typedef struct
    {
        int * intArray;
        float * floatArray;
    } mixedArray;

    // 구조체 포인터 변수 선언
    // 일단 널(null) 값을 갖도록 해야 함. 
    // 이유: 아직 할당될 값을 저장할 공간이 메모리의 힙(heap)에 준비되지 않았음.
    mixedArray * arrMixed = NULL;

    // 힙(heap)에 할당될 구조체의 크기만큼의 영역 할당하기
    // malloc() 함수를 이용하여 선언된 구조체 자료형의 크기만큼만 영역을 할당하도록 함.
    arrMixed = (mixedArray *)malloc(sizeof(mixedArray));

    // 구조체 항목 지정
    arrMixed->intArray = arr1;
    arrMixed->floatArray = arr2;

    // 활용
    printf("%d\n", arrMixed->intArray[0]);
    printf("%.1f\n", arrMixed->floatArray[2]);

    return 0;
}

Python 구현

  • 해당사항 없음