LAB/C

[24.08.13] c언어의 기초 11

it-lab-0130 2024. 8. 13. 15:45
Chapter 14 구조체와 그 밖의 데이터형

 

1. 구조체

- c는 구조체 변수를 제공함으로써, 데이터를 표현하는 능력을 향상 시킨다.

- 구조체를 이용하여 새로운 데이터형을 만들 수 있다.

  • 구조체 선언의 위치 : 구조체 선언이 main 함수 앞에 있으면 프로그램 전체에서 사용할 수 있고, 함수 안에 선언하면 그 함수 안에서만 쓸 수 있다. 구조체 선언이 끝나면 그 이후부터 사용자가 정의한 새로운 자료형을 컴파일러가 인식 할 수 있다.

- 구조체 변수 선언 

  • 구조체 변수를 선언하면 저장공간이 할당됩니다. 각 멤버의 공간이 메모리에 연속으로 할당되며 멤버를 더한 전체 저장 공간이 하나의 구조체 변수가 되므로 변수의 크기는 각 멤버의 크기를 더한 갑이 된다.
  • 구조체 변수에서 별도의 멤버 접근 연산자 .(마침표)가 필요하다.
  • 구조체_변수명.멤버명으로 접근하여 데이터를 입력한다. 

 

*** 구조체 예제1(14.1)

- 도서목록 배열을 만들기 위해 첫 번째로, 책 제목, 저자명, 정간만을 정보에 포함시킨다.

- 도서목록 배열에 한번에 저장할 것이다.

//* 관리하는 도서가 하나뿐인 도서목록 */
#include <stdio.h>
#include <string.h>
char * s_gets(char * st, int n);
#define MAXTITL  41      /* 최대 책 제목 길이 + 1         */
#define MAXAUTL  31      /* 최대 저자명 길이 + 1 */

struct book {            /* 구조체 템플릿 : 태그는 book이다.     */
    char title[MAXTITL];  // 책 제목
    char author[MAXAUTL];  // 저자
    float value;           // 정가
};                       /* 구조체 템플릿 끝          */

int main(void)
{
    struct book library; /* Library를 book형 변수로 선언한다.  */
    
    printf("도서 제목을 입력하세요..\n");
    s_gets(library.title, MAXTITL); /* 책제목(title) 부분에 접근한다.       */
    printf("저자명을 입력하세요.\n");
    s_gets(library.author, MAXAUTL);
    printf("정가를 입력하세요.\n");
    scanf("%f", &library.value);
    printf("%s by %s: $%.2f\n",library.title,
           library.author, library.value);
    printf("%s: \"%s\" ($%.2f)\n", library.author,
           library.title, library.value);
    printf("끝.\n");
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 개행을 찾는다.
        if (find)                  // 주소가 NULL이 아니면,
            *find = '\0';          // null문자를 거기에 둔다.
        else
            while (getchar() != '\n')
                continue;          // 행의 나머지를 삭제
    }
    return ret_val;
}

 

- struct book{ ... } : 안에 든 구조체들을 멤버(Member) 또는 필드(Field)라고 부른다. (구조체 설계 하는 공간)

- struct 안에 char title[MAXTITL] : title 멤버는 MAXTITL개의 원소를 가지는 char형 배열이다.

 

### 구조체 설계는 데이터를 표현하는 방법을 컴파일러에게 알려주지만, 컴퓨터가 그 데이터를 위한 기억 공간을 할당하지 않는다.

 

1-1. 구조체의 초기화

- ex.) struct book library = { "The Pirate and the Devious Damsel", "Renee Vivotte",1,95};

{ }(중괄호)안에 콤마로 분리된 초기화자 리스트를 사용한다.

** 초기화 시 구조체 설계한 데이터형과 일치해야한다.

 

1-2. 구조체 멤버에 접근하기

- .(마침표)연산자를 사용하여 접근 할 수 있다.

ex.) library.value = > library의 value부분을 찾을 수 있다.

 

1-3. 구조체를 위한 지정 초기화자

- 구조체를 위한 지정 초기화자를 제공한다.

ex.) book구조체의 value멤버만 초기화 하려면, struct book surprise = { .value = 10.99};로 사용된다.

지정 초기화자는 순서에 상관없이 사용할 수 있다.

struct book gift = { .value = 25.99, .author = "James Broadfool", .title = "Rue for the Toad"};

 

- 배열과 마찬가지로, 지정 초기화자 뒤에 오늘 일반 초기화자는, 그 지정 멤버 뒤에 오는 멤버에 값을 제공한다. 또한, 특정 멤버에 가장 마지막으로 제공된 값이 그 멤버의 값이 된다.

ex.) struct book gift = { .value = 18.90, .author = "Philionna Pestle", 0.25};

구조체 선언에서 author 멤버 바로 뒤에 value가 나오기 때문에, 값 0.25가 value멤버에 대입된다. 그래서 이전에 제공된 값 18.90은 새로운 값 0.25로 대체된다.

 

1-4. 구조체의 배열

 

*** 구조체의 배열을 써보자 (14.2)

/* 구조체의 배열 여러책 관리하기 */
#include <stdio.h>
#include <string.h>
char * s_gets(char * st, int n);
#define MAXTITL   40
#define MAXAUTL   40
#define MAXBKS   100              /* 책의 최대 권 수  */

struct book {                     /* 구조체 설계 : book 템플릿을 설정한다.     */
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};

int main(void)
{
    struct book library[MAXBKS]; /* book 구조체 배열 선언 */
    int count = 0;
    int index;
    
    printf("도서 제목을 입력하세요.\n");
    printf("끝내려면 라인의 시작 위치에서 [enter]키를 누르세요.\n");
    while (count < MAXBKS && s_gets(library[count].title, MAXTITL) != NULL
           && library[count].title[0] != '\0')
    {
        printf("저자명을 입력하세요.\n");
        s_gets(library[count].author, MAXAUTL);
        printf("정가를 입력하세요.\n");
        scanf("%f", &library[count++].value);
        while (getchar() != '\n')
            continue;          /* 입력 라인을 깨끗이 비운다.        */
        if (count < MAXBKS)
            printf("다음 타이틀을 입력하세요..\n");
    }
    
    if (count > 0)
    {
        printf("다음은 소장하고 있는 도서들의 목록입니다:\n");
        for (index = 0; index < count; index++)
            printf("%s by %s: $%.2f\n", library[index].title,
                   library[index].author, library[index].value);
    }
    else
    	printf("책이 한권도 없나요? 그럴수 있지.\n");
    
    return 0;
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 개행(\n)을 찾는다
        if (find)                  // 주소가 NULL이 아니면, 
            *find = '\0';          // 널 문자를 거기에 놓는다.
        else
            while (getchar() != '\n')
                continue;          // 행의 나머지를 처리한다.
    }
    return ret_val;
}

 

- 구조체 배열을 선언하는 것은, 다른 종류의 배열을 선언하는 것과 같다.

- struct book library[MAXBKS];  

  • MAXBKS개의 원소를 가지는 배열 library를 선언한다. 이배열의 각 원소들은 book형의 구조체이다.

- while (count < MAXBKS && s_gets(library[count].title, MAXTITL) != NULL && library[count].title[0] != '\0')

 

s_gets(library[count].title, MAXTITL);

  • 책 타이틀에 해당하는 문자열을 읽는다.
  • s_gets()가 파일의 끝을 지나쳐 읽으려고 시도하면 그 표현식은 NULL로 평가된다.

library[count].title[0] != '\0' 

  • 입력받은 문자열의 첫 문자가 널 문자인지 (즉, 빈문자열인지) 검사한다.
  • 또한, 입력한 책의 수가 배열의 크기 한계를 초과하지 않도록 검사한다.

 

1-5. 구조체의 배열에서 멤버 식별하기

- library[0].value  >>  첫 번째 배열 원소와 관련된 value멤버 

  library[4].title >> 다섯 번째 배열 원소와 관련된 title멤버

- library[2].title[4]  >> 세 번째 구조체(libraray[2])가 나타내는 책의 타일틀에 있는 다섯 번째 문자(title[4]) >> b 이다.

- 도트 연산자의 오른쪽에서 발견되는 인덱스는 개별 멤버에 적용되고, 왼쪽에서 발견되는 인데스는 구조체의 배열에 적용된다.

더보기

ex.)

library ;                 // book 구조체의 배열 선언

library[2] ;            // 배열의 한 원소, 그러므로 하나의 book 구조체

library[2].title;     // char형 배열, (library[2]의 타이틀 멤버)

library[2].title[4]; // title 멤버 배열의 한 문자.

 


2. 구조체를 가리키는 포인터

- 구조체를 가리키는 포인터는 함수에 전달할 수 있다.

- 구조체르 함수에 전달할 수 있다 해도 포인터를 전달하는 것이 더 효율적이다.

- 다른 구조체를 가리키는 포인터를 멤버로 가지는 구조체를 사용한다.

- 배열과 다르게 구조체의 이름은 구조체의 주소가 아니다 !! 그러므로 & 연산자를 사용해야 한다.

ex.) him = &fellow[0]; (him이 fellow[0]의 주소를 가리키도록 초기화 한다.)

 

*** 구조체를 가리키는 포인터예제 (14.4)

/* 구조체를 가리키는 포인터를 사용한다. */
#include <stdio.h>
#define LEN 20

struct names {
    char first[LEN];
    char last[LEN];
};

struct guy {
    struct names handle;
    char favfood[LEN];
    char job[LEN];
    float income;
};

int main(void)
{
    struct guy fellow[2] = {
        {{ "Ewen", "Villard"},
            "grilled salmon",
            "personality coach",
            68112.00
        },
        {{"Rodney", "Swillbelly"},
            "tripe",
            "tabloid editor",
            432400.00
        }
    };
    struct guy * him;    /* 구조체를 가리키는 포인터 */
    
    printf("주소 #1: %p #2: %p\n", &fellow[0], &fellow[1]);
    him = &fellow[0];    /* 포인터에게 가리킬 곳을 알려준다.  */
    printf("포인터 #1: %p #2: %p\n", him, him + 1);
    printf("him->income 은 $%.2f: (*him).income 은 $%.2f\n",
           him->income, (*him).income);
    him++;               /* 다음 구조체를 가리키게 한다.      */
    printf("him->favfood 는 %s:  him->handle.last 는 %s\n",
           him->favfood, him->handle.last);
    
    return 0;
}

 

- 구조체를 가리키는 포인터를 선언하는 것은 아주 쉽다 .

ㄴ struct guy *him;

 

2-1. 포인터를 사용하여 멤버에 접근하기

 

1. 새로운 연산자 (->)를 사용한다.

  • him -> income은 him == &barney 이면 barney.income 이다.
  • him == &fellow[0]이면, him -> income은 fellow[0].income이다.
  • him은 포인터이고, him -> income은 그포인터가 가리키는 구조체의 한 멤버라는 것이다.
  • him -> income은 float형 변수이다.

2. him == &fellow[0]이면, *him == fellow[0]이다.

  • .(마침표)연산자가 * 연산자보다 우선순위가 높기 때문에 괄호가 필요하다.
  • fellow[0].income == (*him).income 이다.

 


3. 함수에 구조체 알리기

- 최시 컴파일러에서는 함수의 전달인자로 구조체 자체를 전달 할 것인지, 아니면 구조체를 가리키는 포인터를 전달할지 사용자가 선택 할 수 있다.

 

3-1. 구조체의 멤버 전달하기

- 구조체가 하나의 값을 가지는 데이터형 (즉, int형, char형, float형, double형, 또는 포인터형)이라면, 구조체의 멈버를 전달인자로 사용 할 수 있다.

 

*** 구조체의 멤버를 전달인자로 사용예제 (14.5)

/* 구조체의 멤버를 전달인자로 사용한다. */
#include <stdio.h>
#define FUNDLEN 50

struct funds {
    char   bank[FUNDLEN];
    double bankfund;
    char   save[FUNDLEN];
    double savefund;
};

double sum(double, double);

int main(void)
{
    struct funds stan = {
        "국민은행",
        4032.27,
        "카카오뱅크",
        8543.94
    };
    
    printf("Stan 씨의 총 잔고는 $%.2f입니다.\n",
           sum(stan.bankfund, stan.savefund) );
    return 0;
}

/* 두 개의 double형 값을 더한다. */
double sum(double x, double y)
{
    return(x + y);
}

 

- 함수 sum()은 실전달인자들이 구조체의 멤버인지 신경 쓰지 않는다. 다만 그들이 double형일 것을 요구한다.

 

 

 

3-2. 구조체의 주소 사용하기

 

*** 구조체의 주소 예제 (14.6)

/* 구조체를 가리키는 포인터를 전달한다. */
#include <stdio.h>
#define FUNDLEN 50

struct funds {
    char   bank[FUNDLEN];
    double bankfund;
    char   save[FUNDLEN];
    double savefund;
};

double sum(const struct funds *);  /* 전달 인자가 포인터이다. */

int main(void)
{
    struct funds stan = {
        "국민은행",
        4032.27,
        "카카오뱅크",
        8543.94
    };
    
    printf("Stan 씨의 총 잔고는 $%.2f입니다.\n", sum(&stan));
    
    return 0;
}

double sum(const struct funds * money)
{
    return(money->bankfund + money->savefund);
}

- sum()함수는 funds 구조체를 가리키는 하나의 포이터(memory)를 전달인자로 사용한다.

- 주소 &stan을 함수에 전달하면, 포인터 money는 구조체 stan을 가리키게 된다.

- 그리고 나서 함수는 ->(연산자)를 사용하여 stan.bankfund와 stan.savefund의  값을 얻는다.

** 포인터가 가리키는 값의 내용을 함수가 변경하면 안되기 때문에, money가 const를 가리키는 포인터로 선언되었다.

- 프로그램은 money -> bankfund를 사용했다. 포인터 이기 때문에 

 

3-3. 전달인자로 구조체 전달하기 

*** 전달인자로 구조체 전달 예제 (14.7)

/* 구조체 전달하기 */
#include <stdio.h>
#define FUNDLEN 50

struct funds {
    char   bank[FUNDLEN];
    double bankfund;
    char   save[FUNDLEN];
    double savefund;
};

double sum(struct funds moolah);  /* 전달인자가 구조체이다. */

int main(void)
{
    struct funds stan = {
        "국민은행",
        4032.27,
        "카카오뱅크",
        8543.94
    };
    
    printf("Stan 씨의 총 잔고는 $%.2f입니다.\n", sum(stan));
    
    return 0;
}

double sum(struct funds moolah)
{
    return(moolah.bankfund + moolah.savefund);
}

 

- struct funds를 가리키는 포인터인 money를, struct funds변수인 moolah로 대체했다.

- sum()이 호출 됐을때, moolah라는 이름의 자동변수가 funds 템플릿에 따라 만들어진다. 

그러면 이 멤버들은 구조체 stan의 대응하는 멤버들이 가지고있는 값의 복사본으로 각각 초기화 된다.

- 프로그램은 moolah -> bankfund 가 아닌 moolah.bankfund를 사용한다.

 

3-4. 구조체의 그 밖의 특성

- 최신 C는 한 구조체를 다른 구조체에 대입하는 것을 허용한다. 즉, n_data와 o_data가 같은 데이터형의 구조체라면, 가능하다.

o_data = n_data

  • 이것은 o_data의 각 멤버에 n_data의 대응하는 각 멤버의 값이 대입되게 한다.
  • 멤버가 배열일 경우에도 동작한다.

같은 데이터 형의 구조체라면, 한 구조체를 다른 구조체로 초기화 할 수 있다.

  • struct names right_field = {"Ruthie", "George"};
  • struct names captain = right_field; // 한 구조체를 다른 구조체로 초기화

- 함수의 리턴 값으로도 리턴할 수 있다.

 

*** 포인터를 사용하여 구조체를 다루는 간단한 프로그램 (14.8)

- 구조체 포인터의 멤버에 접근할 때는 -> (화살표 연산자arrow operator))를 사용합니다.

/* 구조체를 가리키는 포인터를 사용한다.*/
#include <stdio.h>
#include <string.h>

#define NLEN 30
struct namect {
    char fname[NLEN];
    char lname[NLEN];
    int letters;
};

void getinfo(struct namect *);
void makeinfo(struct namect *);
void showinfo(const struct namect *);
char * s_gets(char * st, int n);

int main(void)
{
    struct namect person;
    
    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
    return 0;
}

void getinfo (struct namect * pst)
{
    printf("이름을 입력하세요.\n");
    s_gets(pst->fname, NLEN);
    printf("성씨를 입력하세요.\n");
    s_gets(pst->lname, NLEN);
}

void makeinfo (struct namect * pst)
{
    pst->letters = strlen(pst->fname) +
    strlen(pst->lname);
}

void showinfo (const struct namect * pst)
{
    printf("%s %s, 당신의 성명은 %d개의 글자를 가지고 있습니다.\n",
           pst->fname, pst->lname, pst->letters);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 개행(\n)을 찾는다.
        if (find)                  // 주소가 NULL이 아니면, 
            *find = '\0';          // 널문자(\0)를 거기에 놓는다.
        else
            while (getchar() != '\n')
                continue;          // 행의 나머지를 처리한다.
    }
    return ret_val;
}

 

- person 구조체의 주소가 함수에 전달된다.

- getinfo() 함수는 그 자체에서 main()에 정보를 전달한다.

  • 사용자로부터 이름과 성씨를 얻어, person 구조체에 넣는다.
  • person 구조체를 pst포인터를 사용하여 찾는다.

- pst -> lname을 char형 배열 이름과 동등한 것으로 만든다. 그러므로 gets()의 전달인자로 사용할 수 있다.

* pst -> lname은 pst가 가리키는 구조체의 lname 멤버를 의미한다!!!

 

- makeinfo()

  • 양방향 정보 전달을 수행한다.
  • makeinfo()는 person을 가리키는 포인터를 사용하여, 그 구조체에 저장된 이름과 성씨를 찾는다.
  • strlen()을 사용하여, 이름과 성씨를 이루고 있는 글자 수를 각각 구하여 더한 후, person의 주소를 사용하여 그 합을 저장한다.

- showinfo() 

  • 포인터를 사용하여 출력할 정보를 찾는다.
  • showinfo()함수가 배열의 내용을 변경시키면 안되기 때문에 , 그 포인터는 const로 선언된다.

- 이 모든 경우에, 하나의 구조체 변수 person이 있고, 각 함수는 구조체의 주소를 사용하여 person에 접근했다.

  1. 구조체 자체를 전달하기 위해, 전달인자로 &person(주소) 대신에 person(구조체 변수)을 사용한다.
  2. 형식매개변수는 구조체를 가리키는 포인터 대신에 struct namect형으로 선언한다.
  3. main()에 구조체 값을 제공하기 위해, 구조체를 리턴할 수 있다.

 

 

*** 포인터를 사용하지 않고 구조체를 다루는 예제 (14.9)

* 다른 방법으로 처리한 것이다.

/* 구조체 자체를 전달하고 리턴한다. */
#include <stdio.h>
#include <string.h>

#define NLEN 30
struct namect {
    char fname[NLEN];
    char lname[NLEN];
    int letters;
};

struct namect getinfo(void);
struct namect makeinfo(struct namect);
void showinfo(struct namect);
char * s_gets(char * st, int n);

int main(void)
{
    struct namect person;
    
    person = getinfo();
    person = makeinfo(person);
    showinfo(person);
    
    return 0;
}

struct namect getinfo(void)
{
    struct namect temp;
    printf("이름을 입력하세요.\n");
    s_gets(temp.fname, NLEN);
    printf("성씨를 입력하세요.\n");
    s_gets(temp.lname, NLEN);
    
    return temp;
}

struct namect makeinfo(struct namect info)
{
    info.letters = strlen(info.fname) + strlen(info.lname);
    
    return info;
}

void showinfo(struct namect info)
{
    printf("%s %s, 당신의 성명은 %d개의 글자를 가지고 있습니다.\n",
           info.fname, info.lname, info.letters);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 개행(\n)을 찾는다.
        if (find)                  // 주소가 NULL이 아니면
            *find = '\0';          // 널 문자를 거기에 놓는다.
        else
            while (getchar() != '\n')
                continue;          // 행의 나머지를 처리한다.
    }
    return ret_val;
}

 

- 세 함수들이 제각기 person의 복사본을 만든다. 그래서 프로그램은 하나가 아니라 서로 구별되는 4개의 구조체를 사용한다.

- makeinfo() 

  • 앞의 버전 (14.8)에서 person의 주소가 전달되었다. 그리고 그 함수는 person의 실제값을 가지고 작업했다.
  • 여기서는 info라는 이름의 새로운 구조체가 생성된다. person에 저장된 값들이 info에 복사된다.

 

### 구조체를 사용할까? 구조체 포인터를 사용할까?? ###

- 구조체 안에 있는 한 두개의 멤버만 사용하는 함수에 커다란 구조체 전체를 전달하는 것은 낭비다.

- 이러한 경우에는, 포인터를 전달하거나 필요한 멤버들만 개별적인 전달인자로 전달하는 것이 바람직하다.

- 효율성으로 구조체 포인터를 함수의 전달인자로 사용한다!!!!!

- 의도하지 않은 데이터 변경을 방지하기 위해 const를 사용한다.

 

### 구조체 안에 문자 배열을 사용할까?? 문자 포인터를 사용할까??  ###

- 구조체에 들어 있는 포인터들은 프로그램의 어딘가에서 생성되고 할당된 문자열들을 관리하는 용도로만 사용해야한다.

- 구조체 안에 문자열을 저장해야 한다면, 문자 배열 멤버를 사용해라!!!! 

- 구조체 안에 char형을 가리키는 포인터를 저장하는 것은 나름대로 용도가 있다.

 

3-5. 구조체, 포인터, malloc()

- 문자열을 다루기 위해 구조체 안에 포인터를 사용하는 것이 바람직한 한 가지 경우가 있다.

- malloc()

  •  동적 메모리를 할당
  • struct 구조체이름 *포인터이름 = malloc(sizeof(struct 구조체이름));
  • 꼭 필요한 만큼의 메모리를 malloc()에게 요청할 수 있다.
  • malloc()을 사용하면 문자열은 구조체 안에 저장되지않고 malloc()이 관리하는 메모리 덩어리에 저장된다.
  • malloc()함수는 free()호출과 짝을 이룬다.
  • malloc()함수를 호출하면 메모리만 할당하며 할당한 메모리의 값을 초기화하지 않는다.
  • 따라서 동적으로 할당받은 메모리의 초기값은 쓰레기값(Gabage Value)가 된다.
  • 그리고 동적으로 할당받은 메모리가 더 이상 필요 없으면 free함수를 호출하여 해제 해야한다.

*** malloc() 예제 (14.10)

// 포인터와 malloc() 사용
#include <stdio.h>
#include <string.h>   //  strcpy(), strlen() 사용하기 위해 선언
#include <stdlib.h>   // malloc(), free() 사용하기 위해 선언
#define SLEN 81
struct namect {
    char * fname;  // 배열 대신에 포인터를 사용한다.
    char * lname;
    int letters;
};

void getinfo(struct namect *);        // 메모리를 할당한다,
void makeinfo(struct namect *);
void showinfo(const struct namect *);
void cleanup(struct namect *);        // 사용이 끝난 메모리를 해제한다.
char * s_gets(char * st, int n);

int main(void)
{
    struct namect person;
    
    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
    cleanup(&person);
    
    return 0;
}

void getinfo (struct namect * pst)
{
    char temp[SLEN];
    printf("이름을 입력하세요.\n");
    s_gets(temp, SLEN);
    // 이름을 저장할 메모리를 할당한다.
    pst->fname = (char *) malloc(strlen(temp) + 1);
    // 할당된 메모리에 이름을 복사한다.
    strcpy(pst->fname, temp);
    printf("성씨를 입력하세요.\n");
    s_gets(temp, SLEN);
    pst->lname = (char *) malloc(strlen(temp) + 1);
    strcpy(pst->lname, temp);
}

void makeinfo (struct namect * pst)
{
    pst->letters = strlen(pst->fname) +
    strlen(pst->lname);
}

void showinfo (const struct namect * pst)
{
    printf("%s %s, 당신의 성명은 %d개의 글자를 가지고 있습니다.\n",
           pst->fname, pst->lname, pst->letters);
}

void cleanup(struct namect * pst)
{
    free(pst->fname);
    free(pst->lname);
}

char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;
    
    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // 개행(\n)을 찾는다.
        if (find)                  // 주소가 NULL이 아니면
            *find = '\0';          // 널 문자를 거기에 놓는다
        else
            while (getchar() != '\n')
                continue;          // 행의 나머지를 처리한다.
    }
    return ret_val;
}

 

- cleanup()이라는 함수를 사용하여 프로그램이 사용을 끝낸 메모리를 해제한다.

 

3-5. 복합 리터럴과 구조체 (C99)

- 복합 리터럴

  • 함수의 전달인자로 사용되는 구조체나 다른 구조체에 대입되는 구조체를 생성하는데 사용할 수 있다.
  • 형식 : 중괄호({ })로 둘러싼 초기화자 리스트 앞에 괄호로 둘러 싼 데이터형 이르을 위치 시킨다.

ex.) (struct book) { "The Idiot", "Fydor Dostoyevsky",6.99}

(괄호 안에 타입을 적고/ 구조체 타입의 book ) { "문자열 타입 데이터", "문자열 타입 데이터", float형 데이터} 

 

*** 복합 리터럴 예제 (14.11)

/* 복합 리터럴 */
#include <stdio.h>
#define MAXTITL  41
#define MAXAUTL  31

struct book {          // 구조체 설계 : book은 태그이다.
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};

int main(void)
{
    struct book readfirst;
    int score;
    
    printf("테스트 스코어를 입력하세요: ");
    scanf("%d",&score);
    
    if(score >= 84)
        readfirst = (struct book) {"Crime and Punishment",
            "Fyodor Dostoyevsky",
            11.25};
    else
        readfirst = (struct book) {"Mr. Bouncy's Nice Hat",
            "Fred Winsome",
            5.99};
    printf("할당된 독서:\n");
    printf("%s by %s: $%.2f\n",readfirst.title,
           readfirst.author, readfirst.value);
    
    return 0;
}

 

- 복합 리터럴은 함수에 전달인자로 사용할 수도 있다. 

- 함수 바깥에 있는 복합 리터럴은 정적 수명을 가진다.

- 복합 리터럴에 지정 초기화자를 사용할 수 있다.

 

### 컴퓨터는 이러한 리터럴들을 따로 모아서 보관한다. ###

프로그램이 실행되서 메모리에 로드되면, 5 가지 종류의 영역(text segment, data segment, bss segment, heap, stack) 이 존재합니다. 이 때, 텍스트 세그먼트(text segment) 에 프로그램 코드와 상수, 리터럴 등이 여기서 정의됩니다. 왜냐하면 텍스트 세그먼트에 있는 내용들은 읽기만 가능하기 때문이지요. 물론 이 사실은 컴파일러 구현에 따라, 사용하는 운영체제 환경에 따라서 다를 수 있습니다.


 

4. 열거형

* 열거형 (enum) : enum 키워드를 사용하여 정의하며 열거, 목록을 뜻하는 Enumeration에서 따왔다.

- 열거형은 정의만 해서는 사용 할 수가 없다. 따라서 열거형도 변수로 선언하여 사용합니다.

- 새로운 데이터 목록을 만들고 그 데이터형이 가질 수 있는 값들을 지정할 수 있다.

- 열거형의 목적은 가독성을 높이는 것이다.

  • enum 열거형이름 변수이름 ; 으로 정의한다.
  • enum 상수들은 사실상 int형이다. int형을 사용할 수 있는 곳이라면 어디라도 사용가능 하다.

ex.) enum DayofWeek {

 Sunday = 0, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

enum spectrum = { red, orange, yellow, green, blue, violet}; 

enum spectrum color; (enum 열거형이름 변수이름; 선언)

- spectrum은 태그이름으로 설정한다. 

 

- 즉, 변수 color는 red, orange, yellow, green, blue, violet를 가질 수 있다. 

이 기호화된 상수들은 '열거된 상수(enumerator)'들이라고 부른다. 

더보기

ex.) 사용해보자~

int c;

color = blue;

if (color == yellow)

....;

for (color = red, color <= violet; color++)

...;

 

red 나 blue같은 열거된 상수들은 int형이지만, 데이터형이 열거된 상수들을 수용할 수 있다면, 열거형 변수로 어떤 정수형을 사용해도 상관없다. 

예를 들어, spectrum에 열거된 상수들은 0에서 5까지의 범위를 가진다. 그래서 컴파일러는 color변수를 unsigned char형으로 나타낼 수 있다.

** C++에서는 열거형 변수에 ++연산자를 사용할 수 없다. 그래서 C와 C++에서 사용하기 위해선 color를 int형으로 선언해야 한다.

 

4-1. enum상수