리스트 14.1 book.c 프로그램
//* book.c -- 관리하는 도서가 하나뿐인 도서 목록 */
#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;
}
<출력>
도서 제목을 입력하시오.
Chiken of the Andes
저자명을 입력하시오.
Disma Lapoult
정가를 입력하시오.
22.99
Chiken of the Andes by Disma Lapoult: $22.99
Disma Lapoult: "Chiken of the Andes" ($22.99)
종료.
<코드분석>
이 프로그램은 도서 한 권의 정보를 관리하는 간단한 C 프로그램이다. 책의 제목, 저자명, 정가(가격)를 입력받고, 이 정보를 화면에 출력한다
1. 헤더 파일 포함
리스트 14.2 manybook.c 프로그램
/* manybook.c -- 여려 권의 책을 관리하는 도서 목록 */
#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'); // 개행을 찾는다
if (find) // 주소가 NULL이 아니면,
*find = '\0'; // 널문자를 거기에 놓는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
<코드분석>
이 프로그램은 여러 권의 책을 관리하는 도서 목록을 만드는 C 프로그램입니다. 책의 제목, 저자명, 그리고 가격을 입력받아 목록으로 저장하고, 나중에 이 목록을 출력하는 기능을 합니다.
1. 헤더파일 정의
14.4 friends.c 프로그램
/* friends.c -- 구조체를 가리키는 포인터를 사용한다. */
#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",
232400.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 is $%.2f: (*him).income is $%.2f\n",
him->income, (*him).income);
him++; /* 다음 구조체를 가리키게 한다 */
printf("him->favfood is %s: him->handle.last is %s\n",
him->favfood, him->handle.last);
return 0;
}
<출력>
주 소 #1: 0x7ffe8811e0a0 #2: 0x7ffe8811e0f4
포인터 #1: 0x7ffe8811e0a0 #2: 0x7ffe8811e0f4
him->income is $68112.00: (*him).income is $68112.00
him->favfood is tripe: him->handle.last is Swillbelly
<코드분석>
이 프로그램은 구조체와 포인터를 사용하는 방법을 보여주는 예제다. 포인터를 사용하면 구조체의 데이터를 쉽게 접근할 수 있다. 이 프로그램에서는 두 명의 사람에 대한 정보를 구조체 배열에 저장하고, 포인터를 이용해 그 정보를 출력한다.
<코드분석>
이 프로그램은 구조체와 함수 전달 인자를 사용하는 방법을 보여주는 간단한 예제이다. 여기서 중요한 점은 구조체의 멤버(즉, 구조체 안에 있는 개별 데이터를) 함수의 인자로 전달해서 그 데이터를 처리하는 방법이다. 이 예제에서는 Stan이라는 사람의 은행과 저축 계좌에 있는 돈을 더해서 총 잔고를 계산한다.
1.헤더파일 포함 및 매크로 정의
리스트 14.6 funds2.c 프로그램
/* funds2.c -- 구조체를 가리키는 포인터를 전달한다. */
#include <stdio.h>
#define FUNDLEN 50
struct funds {
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(const struct funds *); /* argument is a pointer */
int main(void)
{
struct funds stan = {
"국민은행",
3024.72,
"동아상호신용금고",
9237.11
};
printf("Stan 씨의 총 잔고는 $%.2f입니다.\n", sum(&stan));
return 0;
}
double sum(const struct funds * money)
{
return(money->bankfund + money->savefund);
}
<코드분석>
sum()함수는 funds 구조체를 가리키는 하나의 포인터를 전달인자로사용한다.
주소 &stan을 함수에 전달하면 포인터 money는 구조체 stan을 가리키게 된다.
그러고 나서 함수는 -> 연산자를 사용하여 stan.bankfund와 stan.savefund의 값을 얻는다
포인터가 가리키는 값의 내용을 함수가 변경하면 안 되기 때문에 money가 const를 가리키는 포인터로 선언되었다.
리스트 14.7 funde3.c 프로그램
/* funds3.c -- passing a structure */
#include <stdio.h>
#define FUNDLEN 50
struct funds {
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(struct funds moolah); /* argument is a structure */
int main(void)
{
struct funds stan = {
"국민은행",
3024.72,
"동아상호신용금고",
9237.11
};
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의 대응하는 멤버들이 가지고 있는 값의 복사본으로 각각 초기화 된다.
따라서 원본 구조체의 복사본을 사용하여 계산이 이루어진다.
리스트 14.8 names1.c 프로그램
/* names1.c -- 구조체를 가리키는 포인터를 사용한다 */
#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'); // 개행을 찾는다
if (find) // 주소가 NULL이 아니면
*find = '\0'; // 널문자를 거기에 놓는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
<출력>
이름을 입력하시오.
성민
성씨를 입력하시오.
박
성민 박, 당신의 성명은 9개의 글자를 가지고있습니다..
<코드분석>
이 프로그램은 구조체와 포인터를 사용해서 사람의 이름 정보를 관리하는 방법을 보여주는 예제다.
여기서 구조체는 이름과 성, 그리고 그 이름과 성의 총 글자 수를 저장하는 데 사용된다.
포인터를 사용해서 함수들 사이에서 구조체의 데이터를 주고받으며 작업을 수행한다.
1. 기본적인 설정
#include <stdio.h>#include <string.h>
#define NLEN 30
#include <stdio.h>
는 표준 입출력 함수를 사용하기 위해 포함하는 헤더 파일이다.printf
같은 함수가 여기 들어있다.#include <string.h>
는 문자열을 다루는 함수들을 사용하기 위해 포함하는 헤더 파일이다.
이 예제에서는strlen
과strchr
함수를 사용한다.#define NLEN 30
은 이름과 성의 최대 길이를 정의한다. 여기서는 30자로 설정되어 있다.
2. 구조체 정의
이 구조체는 사람의 이름, 성, 그리고 그 이름의 총 글자 수를 저장할 것이다.
struct namect { char fname[NLEN];
char lname[NLEN];
int letters;
};
struct namect
는 이름 정보를 저장하는 구조체이다.char fname[NLEN];
은 사람의 이름을 저장하는 배열이다.char lname[NLEN];
은 사람의 성을 저장하는 배열이다.int letters;
는 이름과 성의 총 글자 수를 저장하는 정수형 변수이다.
3. 함수 선언
void getinfo(struct namect *);
void makeinfo(struct namect *);
void showinfo(const struct namect *);
char * s_gets(char * st, int n);
getinfo(struct namect * pst);
는 이름과 성을 입력받아 구조체에 저장하는 함수이다.makeinfo(struct namect * pst);
는 이름과 성의 총 글자 수를 계산하는 함수이다.showinfo(const struct namect * pst);
는 저장된 정보를 출력하는 함수이다.s_gets(char * st, int n);
는 입력을 받아 개행 문자를 제거하는 함수이다.
4. 메인 함수에서의 동작
int main(void)
{
struct namect person;
getinfo(&person);
makeinfo(&person);
showinfo(&person);
return 0;
}
struct namect person;
은namect
구조체 변수를 선언한다. 이 변수를 통해 사람의 이름 정보를 관리할 것이다.getinfo(&person);
은person
구조체의 주소를getinfo
함수로 전달하여 이름과 성을 입력받는다.makeinfo(&person);
은 이름과 성의 총 글자 수를 계산해서person
구조체에 저장한다.showinfo(&person);
은 계산된 정보를 화면에 출력한다.
5. getinfo
함수
이 함수는 사용자의 이름과 성을 입력받아 구조체에 저장하는 역할을 한다.
void getinfo (struct namect * pst)
{
printf("이름을 입력하시오.\n");
s_gets(pst->fname, NLEN);
printf("성씨를 입력하시오.\n");
s_gets(pst->lname, NLEN);
}
getinfo(struct namect * pst);
함수는namect
구조체를 가리키는 포인터를 인자로 받는다.s_gets(pst->fname, NLEN);
은 이름을 입력받아 구조체의fname
에 저장한다.s_gets(pst->lname, NLEN);
은 성을 입력받아 구조체의lname
에 저장한다.
6. makeinfo
함수
이 함수는 이름과 성의 총 글자 수를 계산하는 역할을 한다.
void makeinfo (struct namect * pst)
{
pst->letters = strlen(pst->fname) +
strlen(pst->lname);
}
makeinfo(struct namect * pst);
함수는namect
구조체를 가리키는 포인터를 인자로 받는다.pst->letters
는strlen(pst->fname)
과strlen(pst->lname)
을 더해서 계산된 글자 수를 저장한다.
7. showinfo
함수
이 함수는 저장된 이름 정보를 화면에 출력하는 역할을 한다.
void showinfo (const struct namect * pst)
{
printf("%s %s, 당신의 성명은 %d개의 글자를 가지고있습니다..\n",
pst->fname, pst->lname, pst->letters);
}
showinfo(const struct namect * pst);
함수는namect
구조체를 가리키는 포인터를 인자로 받는다.const
키워드는 이 함수가 구조체의 데이터를 수정하지 않는다는 것을 의미한다.printf
함수는fname
,lname
,letters
를 출력하여 이름과 성, 그리고 총 글자 수를 화면에 보여준다.
8. s_gets
함수
이 함수는 입력받은 문자열에서 개행 문자를 제거하는 역할을 한다.
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'; // 널문자를 거기에 놓는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
s_gets(char * st, int n);
함수는fgets
로 입력받은 문자열에서 개행 문자를 찾아서 제거한다.strchr(st, '\n');
은 문자열에서 개행 문자를 찾는다.- 만약 개행 문자가 있다면,
*find = '\0';
로 그 자리를 널 문자(\0
)로 바꿔준다. - 만약 개행 문자가 없으면,
while (getchar() != '\n')
루프를 통해 남은 입력을 버퍼에서 제거한다.
9. 프로그램의 전체 동작
이 프로그램은 사용자로부터 이름과 성을 입력받아, 그 정보를 저장하고, 입력받은 이름과 성의 총 글자 수를 계산한 후, 그 결과를 출력한다. 이 과정을 통해 구조체와 포인터의 사용법, 그리고 함수로 구조체 데이터를 처리하는 방법을 배울 수 있다.
리스트 14.9 names2.c 프로그램
/* names2.c -- 구조체 자체를 전달하고 리턴한다. */
#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'); // 개행을 찾는다
if (find) // 주소가 NULL이 아니면
*find = '\0'; // 널 문자를 거기에 놓는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
<코드분석>
makeinfo() 함수는 person에 저장되어 있는 값들이 계산되었을 때 그것은 person이 아니라 info에 저장된다
그러나 리턴메커니즘이 이걸 해결하는데 makeinfo()에 있는 다음과 같은 리턴문은
return info;
main()에 있는
person = makeinfo(person); 에 결합하여
info에 저장된 값들을 person으로 복사한다.
makeinfo()함수는 구조체를 리턴하기 떄문에 struct namect형으로 선언되었다.
리스트 14.10 name3.c 프로그램
// names3.c -- 포인터와 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'); // 개행을 찾는다.
if (find) // 주소가 NULL이 아니면
*find = '\0'; // 널문자를 거기에 놓는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
<출력>
이름을 입력하시오.
soungmin
성씨를 입력하시오.
park
soungmin park, 당신의 성명은 12개의 글자를 가지고있습니다.
<코드분석>
이 프로그램은 C 언어에서 포인터와 동적 메모리 할당을 사용하는 방법을 보여주는 예제이다.
동적 메모리 할당은 프로그램이 실행되는 동안 필요한 만큼의 메모리를 할당받아 사용하는 기술이다.
이 프로그램은 사용자의 이름과 성을 입력받아 동적으로 메모리를 할당하고,
이를 사용한 후에는 메모리를 해제하는 과정을 보여준다.
1. 헤더 파일 포함 및 매크로 정의
#include <stdio.h>
#include <string.h> // strcpy(), strlen()을 사용하기 위해
#include <stdlib.h> // malloc(), free()을 사용하기 위해
#define SLEN 81
#include <stdio.h>
는 표준 입출력 함수를 사용하기 위해 포함하는 헤더 파일이다.printf
,fgets
같은 함수가 여기 포함되어 있다.#include <string.h>
는 문자열 처리 함수들을 사용하기 위해 포함된다. 여기서는strcpy
와strlen
을 사용한다.#include <stdlib.h>
는 동적 메모리 할당과 해제를 위해 필요한 함수들이 들어있는 헤더 파일이다.malloc
과free
가 여기에 포함된다.#define SLEN 81
은 이름과 성을 입력받을 임시 문자열 배열의 크기를 정의한다. 여기서는 81자로 설정되어 있다.
2. 구조체 정의
struct namect {
char * fname; // 배열 대신에 포인터를 사용한다.
char * lname;
int letters;
};
struct namect
는 사람의 이름 정보를 저장하는 구조체이다. 여기서char * fname
과char * lname
은 동적 메모리 할당을 통해 저장될 이름과 성을 가리키는 포인터이다.fname
과lname
은 포인터로, 사용자가 입력한 이름과 성을 동적으로 할당된 메모리 공간에 저장할 것이다.int letters
는 이름과 성의 총 글자 수를 저장한다.
3. 함수 선언
프로그램에서 사용할 함수들을 미리 선언한다.
void getinfo(struct namect *); // 메모리를 할당한다
void makeinfo(struct namect *);
void showinfo(const struct namect *);
void cleanup(struct namect *); // 사용이 끝난 메모리를 해제한다.
char * s_gets(char * st, int n);
getinfo(struct namect * pst);
는 사용자의 이름과 성을 입력받아 메모리를 할당하고, 그 데이터를 구조체에 저장하는 함수이다.makeinfo(struct namect * pst);
는 이름과 성의 총 글자 수를 계산하는 함수이다.showinfo(const struct namect * pst);
는 저장된 정보를 출력하는 함수이다.cleanup(struct namect * pst);
는 사용이 끝난 메모리를 해제하는 함수이다.s_gets(char * st, int n);
는 입력받은 문자열에서 개행 문자를 제거하는 함수이다.
4. 메인 함수에서의 동작
메인 함수는 프로그램의 전체적인 흐름을 관리한다.
int main(void)
{
struct namect person;
getinfo(&person);
makeinfo(&person);
showinfo(&person);
cleanup(&person);
return 0;
}
struct namect person;
은namect
구조체 변수를 선언한다. 이 변수에 사람의 이름과 성 정보를 저장한다.getinfo(&person);
는person
구조체에 사용자의 이름과 성을 입력받아 메모리를 할당하고 저장하는 함수이다.makeinfo(&person);
는 이름과 성의 총 글자 수를 계산하는 함수이다.showinfo(&person);
는 계산된 정보를 화면에 출력한다.cleanup(&person);
는 동적으로 할당된 메모리를 해제하는 함수이다. 이 작업은 메모리 누수를 방지하기 위해 꼭 필요하다.
5. getinfo 함수
이 함수는 사용자의 이름과 성을 입력받아 메모리를 할당하고, 그 데이터를 구조체에 저장하는 역할을 한다.
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);
}
getinfo(struct namect * pst);
함수는namect
구조체를 가리키는 포인터를 인자로 받는다.char temp[SLEN];
은 임시로 사용자의 입력을 저장할 배열이다.pst->fname = (char *) malloc(strlen(temp) + 1);
는 입력된 이름의 길이에 맞게 메모리를 동적으로 할당한다.+1
은 null 문자를 위한 공간이다.strcpy(pst->fname, temp);
는 할당된 메모리에 이름을 복사한다.- 성에 대해서도 동일한 과정을 거친다.
6. makeinfo 함수
이 함수는 이름과 성의 총 글자 수를 계산하는 역할을 한다.
void makeinfo (struct namect * pst)
{
pst->letters = strlen(pst->fname) + strlen(pst->lname);
}
makeinfo(struct namect * pst);
함수는namect
구조체를 가리키는 포인터를 인자로 받는다.pst->letters
는strlen(pst->fname)
과strlen(pst->lname)
을 더해서 이름과 성의 총 글자 수를 계산하고 저장한다.
7. showinfo 함수
이 함수는 저장된 이름과 성, 그리고 글자 수를 화면에 출력하는 역할을 한다.
void showinfo (const struct namect * pst)
{
printf("%s %s, 당신의 성명은 %d개의 글자를 가지고있습니다.\n",
pst->fname, pst->lname, pst->letters);
}
showinfo(const struct namect * pst);
함수는namect
구조체를 가리키는 포인터를 인자로 받는다.printf
함수를 사용해 이름, 성, 그리고 총 글자 수를 화면에 출력한다.
8. cleanup 함수
이 함수는 동적으로 할당된 메모리를 해제하는 역할을 한다.
void cleanup(struct namect * pst)
{
free(pst->fname);
free(pst->lname);
}
cleanup(struct namect * pst);
함수는namect
구조체를 가리키는 포인터를 인자로 받는다.free(pst->fname);
와free(pst->lname);
는 동적으로 할당된 메모리를 해제한다. 이 작업을 통해 메모리 누수를 방지할 수 있다.
9. s_gets 함수
이 함수는 입력받은 문자열에서 개행 문자를 제거하는 역할을 한다.
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'; // 널문자를 거기에 놓는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
s_gets(char * st, int n);
함수는fgets
로 입력받은 문자열에서 개행 문자를 찾아서 제거한다.strchr(st, '\n');
은 문자열에서 개행 문자를 찾는다.- 만약 개행 문자가 있다면,
*find = '\0';
로 그 자리를 널 문자(\0
)로 바꿔준다. - 만약 개행 문자가 없으면,
while (getchar() != '\n')
루프를 통해 남은 입력을 버퍼에서 제거한다.
10. 프로그램의 전체 동작
이 프로그램은 사용자로부터 이름과 성을 입력받아, 그 길이에 맞게 메모리를 동적으로 할당한 후,
입력받은 데이터를 저장한다. 저장된 데이터는 이름과 성의 총 글자 수와 함께 출력되며,
마지막으로 사용이 끝난 메모리는 해제된다.
리스트 14.11 complit.c 프로그램
/* complit.c -- 복합 리터럴 */
#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;
}
<코드분석>
이 프로그램은 C 언어에서 복합 리터럴을 사용하는 방법을 보여주는 예제이다. 복합 리터럴은 특정 조건에 따라 구조체를 간단하게 초기화할 수 있게 해준다. 이 프로그램은 테스트 점수에 따라 읽어야 할 책을 선택하고, 그 책의 정보를 출력한다. 하나씩 자세히 설명해줄게.
리스트 14.15 enum.c 프로그램
<코드분석>
프로그램은 C 언어에서 **열거형(enum)**을 사용해서 색상 목록을 만들고, 사용자가 입력한 색상이 그 목록에 있는지 확인하는 예제다. 프로그램은 사용자가 입력한 색상을 확인하고, 그에 맞는 메시지를 출력한다.
1. 헤더 파일 포함과 열거형 정의
#include <stdio.h>
#include <string.h> // strcmp(), strchr() 사용하기위해
#include <stdbool.h> // C99 기능
#include <stdio.h>
는 표준 입출력 함수들을 사용하기 위해 필요한 헤더 파일이다.printf
,fgets
같은 함수들이 이 파일에 포함돼 있다.#include <string.h>
는 문자열을 다루기 위한 함수들을 사용하기 위해 포함된다.strcmp
와strchr
같은 함수가 여기에 들어있다.#include <stdbool.h>
는 C99 표준에서 도입된bool
자료형을 사용하기 위해 포함된다.
enum spectrum {red, orange, yellow, green, blue, violet};
const char * colors[] = {"red", "orange", "yellow",
"green", "blue", "violet"};
#define LEN 30
enum spectrum
은 색상들을 열거형으로 정의한 것이다.red
,orange
,yellow
,green
,blue
,violet
과 같은 색상들을 숫자 0부터 순서대로 정의한다. 예를 들어,red
는 0,orange
는 1,yellow
는 2 이런 식이다.const char * colors[]
는 열거형과 대응되는 문자열 배열이다. 여기서"red"
는colors[0]
,"orange"
는colors[1]
이런 식으로 연결된다.#define LEN 30
은 사용자로부터 입력받을 문자열의 최대 길이를 30으로 제한한다.
2. 메인 함수
int main(void)
{
char choice[LEN];
enum spectrum color;
bool color_is_found = false;
char choice[LEN];
는 사용자가 입력한 색상을 저장할 문자열이다.enum spectrum color;
는 열거형 변수로, 사용자가 입력한 색상이 열거형에서 어떤 위치에 있는지 저장한다.bool color_is_found = false;
는 입력된 색상이 목록에 있는지 여부를 저장하는 불리언 변수다. 처음에는false
로 설정해둔다.
3. 사용자 입력 받기 및 색상 찾기
puts("컬러를 입력하시오 (끝내려면 빈 라인):");
while (s_gets(choice, LEN) != NULL && choice[0] != '\0')
{
for (color = red; color <= violet; color++)
{
if (strcmp(choice, colors[color]) == 0)
{
color_is_found = true;
break;
}
}
puts("컬러를 입력하시오 (끝내려면 빈 라인):");
는 사용자에게 색상을 입력하라는 메시지를 출력한다.s_gets(choice, LEN)
는 사용자의 입력을 받아서choice
배열에 저장한다.for (color = red; color <= violet; color++)
루프는 열거형의 모든 색상을 순회하면서 사용자가 입력한choice
와 일치하는지 확인한다.strcmp(choice, colors[color]) == 0
는 사용자가 입력한 색상(choice
)과colors
배열의 값이 동일한지 비교한다. 동일하면color_is_found
를true
로 설정하고 루프를 탈출한다.
4. 결과 출력
if (color_is_found)
switch(color)
{
case red : puts("장미는 red.");
break;
case orange : puts("양귀비는 orange.");
break;
case yellow : puts("해바라기는 yellow.");
break;
case green : puts("잔디는 green.");
break;
case blue : puts("블루벨은 blue.");
break;
case violet : puts("제비꽃은 violet.");
break;
}
else
printf("컬러는 잘 모르겠습니다. %s.\n", choice);
color_is_found = false;
puts("다음 컬러를 입력하시오 (끝내려면 빈 라인):");
}
puts("안녕!");
if (color_is_found)
는 사용자가 입력한 색상이 목록에 있을 경우, 해당 색상에 대한 메시지를 출력한다.switch(color)
문은color
의 값에 따라 다른 메시지를 출력한다. 예를 들어,color
가red
이면"장미는 red."
를 출력한다.else
블록은 입력한 색상이 목록에 없을 때 실행된다."컬러는 잘 모르겠습니다."
라는 메시지를 출력한다.color_is_found = false;
는 다음 입력을 위해 다시 초기화한다.- 루프가 종료되면
"안녕!"
메시지를 출력하고 프로그램이 종료된다.
5. 입력 처리 함수
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'; // 널 문자를 거기에 놓는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
s_gets
함수는fgets
를 사용해 사용자로부터 입력을 받아 처리한다.- 개행 문자가 입력되면 그것을 null 문자(
\0
)로 바꿔 문자열의 끝을 표시한다. - 만약 개행 문자가 입력되지 않았다면, 나머지 입력을 버퍼에서 제거한다.
6. 전체적인 프로그램 동작
이 프로그램은 사용자가 입력한 색상이 미리 정의된 색상 목록에 있는지 확인하고, 해당 색상에 대한 메시지를 출력하는 프로그램이다. 열거형을 사용하여 색상 목록을 관리하고, 사용자의 입력을 비교하여 해당 색상을 찾는다. 프로그램이 종료될 때까지 반복적으로 색상을 입력받을 수 있다.
typedef와 #define의 차이점
typedef
와 #define
은 C 언어에서 사용하는 두 가지 중요한 기능이다. 이 둘은 비슷한 역할을 하지만, 사용하는 방법과 목적이 조금 다르다.
이렇게 #define
과 typedef
는 각각의 역할과 용도가 다르다. 상황에 맞게 적절히 사용하면, 더 효율적이고 가독성이 좋은 코드를 작성할 수 있다.
리스트 14.16 func_ptr.c 프로그램
// func_ptr.c -- 함수 포인터를 사용한다.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LEN 81
char * s_gets(char * st, int n);
char showmenu(void);
void eatline(void); // 라인의 끝까지 읽는다.
void show(void (* fp)(char *), char * str);
void ToUpper(char *); // 문자열을 대문자로 변환한다.
void ToLower(char *); // 문자열을 소문자로 변환한다.
void Transpose(char *); // 대소문자 교차 변환한다.
void Dummy(char *); // 원본 문자열을 그대로 둔다.
int main(void)
{
char line[LEN];
char copy[LEN];
char choice;
void (*pfun)(char *); // char * 전달인자를 사용하고
// 리턴값이 없는 함수를 가리킨다
puts("문자열을 입력하시오 (끝내려면 빈 라인):");
while (s_gets(line, LEN) != NULL && line[0] != '\0')
{
while ((choice = showmenu()) != 'n')
{
switch (choice ) // switch가 포인터를 설정한다
{
case 'u' : pfun = ToUpper; break;
case 'l' : pfun = ToLower; break;
case 't' : pfun = Transpose; break;
case 'o' : pfun = Dummy; break;
}
strcpy(copy, line);// show()에 전달할 문자열을 복사한다
show(pfun, copy); // 선택된 함수를 사용한다
}
puts("문자열을 입력하시오 (끝내려면 빈 라인):");
}
puts("안녕!");
return 0;
}
char showmenu(void)
{
char ans;
puts("메뉴에서 원하는 직업을 선택하시오:");
puts("u) 대문자로 변환 l) 소문자로 변환");
puts("t) 대문자 교차 변환 o) 원본을 그대로");
puts("n) 다음 문자열");
ans = getchar(); // 사용자로부터 응답을 받는다
ans = tolower(ans); // 소문자로 변환한다
eatline(); // 라인의 나머지를 읽어서 폐기한다
while (strchr("ulton", ans) == NULL)
{
puts("a u, l, t, o, or n 중에서 어느 하나를 선택하시오:");
ans = tolower(getchar());
eatline();
}
return ans;
}
void eatline(void)
{
while (getchar() != '\n')
continue;
}
void ToUpper(char * str)
{
while (*str)
{
*str = toupper(*str);
str++;
}
}
void ToLower(char * str)
{
while (*str)
{
*str = tolower(*str);
str++;
}
}
void Transpose(char * str)
{
while (*str)
{
if (islower(*str))
*str = toupper(*str);
else if (isupper(*str))
*str = tolower(*str);
str++;
}
}
void Dummy(char * str)
{
// 문자열을 변경하지 않고 그대로 둔다
}
void show(void (* fp)(char *), char * str)
{
(*fp)(str); // 선택된 함수를 str에 적용한다
puts(str); // 결과를 표시한다
}
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'; // 널 문자를 넣는다
else
while (getchar() != '\n')
continue; // 행의 나머지를 처리한다.
}
return ret_val;
}
<출력>
문자열을 입력하시오 (끝내려면 빈 라인):
Does C make you feel loopy?
메뉴에서 원하는 직업을 선택하시오:
u) 대문자로 변환 l) 소문자로 변환
t) 대문자 교차 변환 o) 원본을 그대로
n) 다음 문자열
t
dOES c MAKE YOU FEEL LOOPY?
메뉴에서 원하는 직업을 선택하시오:
u) 대문자로 변환 l) 소문자로 변환
t) 대문자 교차 변환 o) 원본을 그대로
n) 다음 문자열
l
does c make you feel loopy?
메뉴에서 원하는 직업을 선택하시오:
u) 대문자로 변환 l) 소문자로 변환
t) 대문자 교차 변환 o) 원본을 그대로
n) 다음 문자열
n
문자열을 입력하시오 (끝내려면 빈 라인):
안녕!
<코드분석>
이 프로그램은 C 언어에서 함수 포인터를 사용하는 예제다. 함수 포인터는 말 그대로 함수를 가리키는 포인터다. 이 프로그램에서는 사용자가 입력한 문자열을 다양한 방식으로 변환할 수 있는데, 함수 포인터를 사용해서 어떤 변환을 할지 선택하고 그 함수를 호출한다
1. 헤더 파일과 매크로 정의
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LEN 81
#include <stdio.h>
: 표준 입출력 함수들을 사용하기 위해 필요한 헤더 파일이다. 예를 들어printf
,fgets
같은 함수들이 이 파일에 들어 있다.#include <string.h>
: 문자열 처리 함수들을 사용하기 위해 포함한다. 예를 들어strcpy
,strchr
같은 함수들이 여기에 포함되어 있다.#include <ctype.h>
: 문자 처리 함수들을 사용하기 위해 포함한다. 예를 들어toupper
,tolower
같은 함수들이 여기에 있다.#define LEN 81
: 문자열의 최대 길이를 81자로 정의한다.
2. 메인 함수
int main(void)
{
char line[LEN];
char copy[LEN];
char choice;
void (*pfun)(char *); // char * 인자를 사용하고 리턴값이 없는 함수를 가리킨다
char line[LEN];
: 사용자가 입력한 문자열을 저장할 배열이다.char copy[LEN];
: 원본 문자열을 복사해서 변환 작업을 수행할 배열이다.char choice;
: 사용자가 선택한 메뉴 옵션을 저장할 변수이다.void (*pfun)(char *);
: 함수 포인터이다. 이 포인터는char *
를 인자로 받고, 반환값이 없는 함수를 가리킬 수 있다.
3. 메뉴를 보여주는 함수
char showmenu(void)
{
char ans;
puts("메뉴에서 원하는 작업을 선택하시오:");
puts("u) 대문자로 변환 l) 소문자로 변환");
puts("t) 대소문자 교차 변환 o) 원본을 그대로");
puts("n) 다음 문자열");
ans = getchar(); // 사용자로부터 응답을 받는다
ans = tolower(ans); // 소문자로 변환한다
eatline(); // 라인의 나머지를 읽어서 제거한다
while (strchr("ulton", ans) == NULL)
{
puts("u, l, t, o, 또는 n 중에서 하나를 선택하시오:");
ans = tolower(getchar());
eatline();
}
return ans;
}
showmenu()
함수는 사용자에게 선택할 수 있는 메뉴를 보여주고, 선택된 옵션을 반환한다.getchar()
함수로 사용자가 입력한 문자를 받아온 다음,tolower()
를 사용해 소문자로 변환한다.eatline()
함수는 입력 버퍼에 남아 있는 나머지 문자를 제거해준다. 이렇게 하면 다음 입력이 깨끗하게 들어올 수 있다.while (strchr("ulton", ans) == NULL)
문에서 입력된 문자가 유효한지 확인한다. 유효하지 않으면 다시 입력을 받는다.
4. 문자열 변환 함수들
void ToUpper(char * str)
{
while (*str)
{
*str = toupper(*str);
str++;
}
}
void ToLower(char * str)
{
while (*str)
{
*str = tolower(*str);
str++;
}
}
void Transpose(char * str)
{
while (*str)
{
if (islower(*str))
*str = toupper(*str);
else if (isupper(*str))
*str = tolower(*str);
str++;
}
}
void Dummy(char * str)
{
// 문자열을 변경하지 않고 그대로 둔다
}
ToUpper(char * str)
함수는 문자열을 대문자로 변환한다.ToLower(char * str)
함수는 문자열을 소문자로 변환한다.Transpose(char * str)
함수는 소문자는 대문자로, 대문자는 소문자로 변환한다.Dummy(char * str)
함수는 문자열을 그대로 두고 아무 작업도 하지 않는다.
5. 결과를 보여주는 함수
void show(void (* fp)(char *), char * str)
{
(*fp)(str); // 선택된 함수를 문자열에 적용한다
puts(str); // 결과를 출력한다
}
show(void (* fp)(char *), char * str)
함수는 함수 포인터 fp
가 가리키는 함수를 호출하여, 변환된 문자열을 출력한다.
6. 입력 처리 함수
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) // 개행 문자가 있다면
*find = '\0'; // 널 문자로 바꾼다
else
while (getchar() != '\n')
continue; // 남은 문자를 제거한다
}
return ret_val;
}
7. 전체적인 프로그램 흐름
이 프로그램은 사용자가 입력한 문자열을 다양한 방식으로 변환할 수 있게 해준다. 사용자가 메뉴에서 선택한 작업에 따라 문자열을 대문자로, 소문자로, 또는 대소문자를 교차하여 변환할 수 있다. 함수 포인터를 사용해서 선택된 함수를 호출하고, 그 결과를 출력하는 유연한 프로그램이다.
'스터디 > C언어' 카테고리의 다른 글
챕터 11 문자열과 문자열 함수 (0) | 2024.08.13 |
---|---|
챕터 10 배열과 포인터 (0) | 2024.08.12 |
챕터 9 함수 (0) | 2024.08.10 |
챕터 7 C의 제어문: 분기와 점프 (0) | 2024.08.09 |
챕터 6 C의 제어문: 루프 (0) | 2024.08.09 |