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에 접근했다.
- 구조체 자체를 전달하기 위해, 전달인자로 &person(주소) 대신에 person(구조체 변수)을 사용한다.
- 형식매개변수는 구조체를 가리키는 포인터 대신에 struct namect형으로 선언한다.
- 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상수
'LAB > C' 카테고리의 다른 글
C언어 구조체 열거형 / 동적메모리 할당 (0) | 2024.09.01 |
---|---|
C언어 파일 입출력 (0) | 2024.09.01 |
[24.08.12] c언어의 기초10 (1) | 2024.08.12 |
[24.08.11] c언어의 기초 9 (1) | 2024.08.11 |
[24.08.10] c언어의 기초8 (0) | 2024.08.10 |