chapter11 프로그래밍 연습

milpy·2022년 7월 27일
0

C 기초 플러스 6판

목록 보기
18/18

01

(공백(blank), 탭, 개행을 포함하여) 입력에서 다음 n개까지의 문자들을 읽고, 주소가 전달인자로 전달되는 배열에 그 결과를 저장하는 함수를 작성하고 테스트 하라.

#include <stdio.h>

#define SIZE 20

char *s_gets(char *st, int n);

int main(void)
{
	char str[SIZE];

	s_gets(str,SIZE);

	puts(str);
}

char *s_gets(char *st, int n)
{
	char *ret_val;

	ret_val = fgets(st, n, stdin);

	return ret_val;
}

02

n개의 문자들을 읽은 후에 또는 첫 번째 공백, 탭, 개행을 만난 후에 (어느 것이 먼저 일어나더라도) 읽기를 멈추도록 연습01에서 작성한 함수를 수정하고 테스트 하라.

#include <stdio.h>
#include <ctype.h>

#define SIZE 20

char *s_gets(char *st, int n);

int main(void)
{
	char str[SIZE];

	s_gets(str,SIZE);

	puts(str);
}

char *s_gets(char *st, int n)
{
	char *ret_val;
	int i = 0;

	ret_val = fgets(st, n, stdin);

	if(ret_val)
	{
		while(st[i] != '\0' && !isspace(st[i]))
			i++;
		
		if(st[i] != '\0')
			st[i] = '\0';
		else
			while(getchar() != '\n')
				continue;
	}

	return ret_val;
}

03

한 라인의 입력에서 첫 번째 단어만 읽어 배열에 저장하고, 나머지는 버리는 함수를 작성하고 테스트하라. 선두의 화이트 스페이스는 건너뛰어야 한다. 여기서 단어는 스페이스, 탭, 개행이 들어 있지 않는 연속된 문자들을 말한다.

#include <stdio.h>
#include <ctype.h>

#define SIZE 20

char *s_gets(char *st, int n);

int main(void)
{
	char str[SIZE];

	s_gets(str,SIZE);

	puts(str);
}

char *s_gets(char *st, int n)
{
	char *ret_val;
	int i = 0;

	ret_val = fgets(st, n, stdin);

	if(ret_val)
	{
		while(st[i] != '\0' && !isspace(st[i]))
			i++;
		
		if(st[i] != '\0')
			st[i] = '\0';
		else
			while(getchar() != '\n')
				continue;
	}

	return ret_val;
}

04

프로그래밍 연습문제 03에서 설명한 함수를, 읽을 수 있는 최대 문자수를 지정하는 제2 매개변수를 받아들이는 것을 제외하여 작성하고 테스트하라.

#include <stdio.h>
#include <ctype.h>

#define size 20

char *s_gets(char *st);

int main(void)
{
	char str[size];

	s_gets(str);

	puts(str);
}

char *s_gets(char *st)
{
	char *ret_val;
	int i = 0;

	ret_val = fgets(st, size, stdin);

	if(ret_val)
	{
		while(*st == ' ')
			st++;

		while(st[i] != '\0' && !isspace(st[i]))
			i++;
		
		if(st[i] != '\0')
			st[i] = '\0';
		else
			while(getchar() != '\n')
				continue;
	}

	return ret_val;
}

05

첫 번째 함수 매개변수로 지정되는 문자열에서 두 번째 함수 매개변수로 지정하는 문자가 처음 나타내는 위치를 찾는 함수를 작성하고 테스트 하라. 찾는데 성공하면, 함수는 그 문자를 가리키는 포인터를 리턴한다. (이것은 라이브러리함수 strchr()가 수행하는 기능과 같다.) 루프를 사용하여 함수에 입력 값들을 반복적으로 제공하는 완전한 프로그램을 만들어 작성한 함수를 테스트하라.

#include <stdio.h>

#define SIZE 20

char * str_chr(char *str, char ch);

int main(void)
{
	char str[SIZE];


	char *res_fgets;
	char find_ch = 'j';
	char *result_str;

	while(1)
	{
		res_fgets = fgets(str, SIZE, stdin);
		if (*res_fgets == '\0' || *res_fgets == '\n')
			break;

		putchar(find_ch);
		putchar('\n');

		result_str = str_chr(str, find_ch);

		if(result_str)
			puts(result_str);
		else
			puts("문자를 찾지 못했습니다.");
	}

	return 0;
}

char *str_chr(char *str, char ch)
{
	char *fch = str;

	while(*fch != ch)
	{
		if(*fch == '\0' || *fch == '\n')
			return NULL;
		fch++;
	}

	return fch;
}

06

하나의 문자와 하나의 문자열 포인터를 두 개의 함수 매개변수로 사용하는 is_within() 함수를 작성하라. 그 함수는 문자열 안에 그 문자가 들어 있으면 0이 아닌 값(참)을 리턴하고, 그렇지 않으면 0(거짓)을 리턴한다. 루프를 사용하여 함수에 입력값들을 반복적으로 제공하는 완전한 프로그램을 만들어 작성한 함수를 테스트하라.

#include <stdio.h>

#define SIZE 20

int is_within(char ch, char *str);

int main(void)
{
	char str[SIZE];

	char *res_fgets;
	char find_ch = 'j';
	int result;

	while(1)
	{
		res_fgets = fgets(str, SIZE, stdin);
		if (*res_fgets == '\0' || *res_fgets == '\n')
			break;

		putchar(find_ch);
		putchar('\n');

		result = is_within(find_ch, str);

		if(result)
			puts("찾는 문자가 존재합니다.");
		else
			puts("문자를 찾지 못했습니다.");
	}

	return 0;
}

int is_within(char ch, char *str)
{
	int result = 0;

	while(*str != ch)
	{
		if(*str == '\0' || *str == '\n')
			return 0;
		str++;
	}

	return 1;
}

07

strncpy(s1, s2, n) 함수는 s2에서 s1으로 정확히 n개의 문자들을 복사한다. 필요하면 s2를 자르거나 여분의 널 문자들로 채운다. 타깃 문자열은 s2의 길이가 n이거나 그 이상이면 널 문자로 종료되지 않을 수도 있다. 이 함수는 s1을 리턴한다. 이와 동일한 기능을 수행하는 함수를 직접 작성하고 mystrncpy()라고 부르라. 루프를 사용하여 함수에 입력 값들을 반복적으로 제공하는 완전한 프로그램을 만들어 작성한 함수를 테스트 하라.

#include <stdio.h>

#define SIZE 20

char *mystrncpy(char *s1, char *s2, int n);

int main(void)
{
	char s1[SIZE], s2[SIZE];
	char *res_fgets;
	char *result_str;

	while(1)
	{
		res_fgets = fgets(s2, SIZE, stdin);

		if(*res_fgets == '\0' || *res_fgets == '\n')
			break;

		result_str = mystrncpy(s1, s2, SIZE);

		puts(result_str);
	}

	return 0;
}

char *mystrncpy(char *s1, char *s2, int n)
{
	int i = 0;

	while(1)
	{
		if(i < n && s2[i] != '\0')
		{
			s1[i] = s2[i];
		}
		else
		{
			if(s2[i] == '\0')
			{
				while(getchar() != '\n')
					continue;
			}
			s1[i] = '\0';
			break;
		}
		i++;
	}

	return s1;
	
}

08

두 개의 문자열 포인터를 전달인자로 사용하는 함수 string_in()를 작성하라. 두 번째 문자열이 첫 번째 문자열 안에 있으면, 포함된 문자열이 시작되는 위치의 주소를 리턴한다. 예를 들어, string_in("hats", "at")은 hats에 있는 a의 주소를 리턴한다. 그렇지 않으면 널 포인터를 리턴한다. 루프를 사용하여 함수에 입력 값들을 반복적으로 제공하는 완전한 프로그램을 만들어 작성한 함수를 테스트하라.

#include <stdio.h>

#define SIZE 20

char *string_in(char *base_str, char *find_str);

int main(void)
{
	char base_str[SIZE];
	char find_str[SIZE] = "at";

	char *res_fgets;
	char *result_str;

	while(1)
	{
		res_fgets = fgets(base_str, SIZE, stdin);

		if(*res_fgets == '\0' || *res_fgets == '\n')
			break;

		result_str = string_in(base_str, find_str);

		if(result_str)
			puts(result_str);
		else
			puts("문자를 찾지 못했습니다.");
	}

	return 0;
}

char *string_in(char *base_str, char *find_str)
{
	while(*base_str != '\0')
	{
		int index = 0;

		while(base_str[index] == find_str[index])
		{
			if(base_str[index] == '\0'
			|| base_str[index] == '\n')
				break;

			index++;
		}
		if(find_str[index] == '\0'
		|| find_str[index] == '\n')
			return base_str;

		base_str++;
	}
	return NULL;
}

09

문자열의 내용을 거꾸로 뒤집은 문자열로 대체하는 함수를 작성하라. 루프를 사용하여 함수에 입력을 반복적으로 제공하는 완전한 프로그램을 만들어 작성한 함수를 테스트하라.

#include <stdio.h>

#define SIZE 20

void revers_str(char *str);

int main(void)
{
	char str[SIZE];
	char *res_fgets;

	while(1)
	{
		res_fgets = fgets(str, SIZE, stdin);

		if(*res_fgets == '\0' || *res_fgets == '\n')
			break;

		revers_str(str);

		puts(str);
	}

	return 0;
}

void revers_str(char *str)
{
	char temp_ch;
	int size = 0;

	while(str[size] != '\0' && str[size] != '\n')
		size++;

	if(str[size] == '\n')
		str[size] = '\0';

	size--;
	for(int i = 0; i <= size/2; i++)
	{
		temp_ch = str[i];
		str[i] = str[size - i];
		str[size - i] = temp_ch;
	}
}

10

문자열을 전달인자로 취하여, 그 문자열에 있는 스페이스들을 모두 제거하는 함수를 작성하라. 빈 라인을 입력할 때까지 라인들을 반복하여 읽는 루프를 사용하는 프로그램을 작성하여 그 함수를 테스트 하라. 프로그램은 그 함수를 각 입력 문자열에 적용하여, 그 결과를 표시해야 한다.

#include <stdio.h>
#include <ctype.h>

#define SIZE 20

void del_space(char *str);

int main(void)
{
	char str[SIZE];
	char *res_fgets;

	while(1)
	{
		res_fgets = fgets(str, SIZE, stdin);

		if(*res_fgets == '\0' || *res_fgets == '\n')
			break;

		del_space(str);

		puts(str);
	}
}

void del_space(char *str)
{
	int n = 0;

	while(str[n] != '\0')
	{
		if(isspace(str[n]))
		{
			str[n] = str[n+1];
			if(str[n] == '\0')
				return;
			else
			{
				str[n+1] = ' ';
				del_space(&(str[n+1]));
			}
		}
		n++;
	}
}

11

문자열을 10개까지 또는 EOF를 만날 때까지 읽는 프로그램을 작성하라. 둘중 어느 것이 먼저 일어나더라도 읽기를 멈춘다. 이 프로그램은 사용자가 메뉴로 다음과 같은 5가지 선택권을 제공해야 한다.

1. 문자열들의 원본리스트를 출력한다.
2. 문자열들을 ASCII 조회 순서로 출력한다.
3. 문자열들을 길이가 커지는 순서로 출력한다.
4. 문자열들을 첫 단어의 길이가 커지는 순서로 출력한다.
5. 프로그램을 종료한다.

이 프로그램은 사용자가 종료를 선택할 때까지 계속 실행되어야 한다. 물론 이 프로그램은 메뉴에 제시된 작업을 실제로 수행해야 한다.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define LENGTH 10
#define SIZE 40

int print_menu(void);

void print_origin(char (*str)[SIZE]);
void print_orderby_ascii(char (*str)[SIZE]);
void print_orderby_length(char (*str)[SIZE]);
void print_orderby_first_word_length(char (*str)[SIZE]);

int main(void)
{
	char str[LENGTH][SIZE];
	int eof_flag = 0;
	char menu_choose = 0;

	for(int i = 0; i < LENGTH; i++)
	{
		if(eof_flag == 0)
			eof_flag = scanf("%s", str[i]);
		
        if(eof_flag == EOF)
		{
			for(int j = 0; j < SIZE; j++)
				str[i][j] = '\0';
		}
		else
		{
			eof_flag = 0;
		}
	}

	while(menu_choose != 5)
	{
		menu_choose = print_menu();

		switch(menu_choose)
		{
			case 1:
				print_origin(str);
				break;
			case 2:
				print_orderby_ascii(str);
				break;
			case 3:
				print_orderby_length(str);
				break;
			case 4:
				print_orderby_first_word_length(str);
				break;
		}
	}

	return 0;
}

int print_menu(void)
{
	int choose;

	for(int i = 0; i < 40; i++)
		putchar('/');
	putchar('\n');

	printf("1. 문자열들의 원본리스트를 출력한다.\n");
	printf("2. 문자열들을 ASCII 조회 순서로 출력한다.\n");
	printf("3. 문자열들을 길이가 커지는 순서로 출력한다.\n");
	printf("4. 문자열들을 첫 단어의 길이가 커지는 순서로 출력한다.\n");
	printf("5. 프로그램을 종료한다.\n");
	
	while(1)
	{
		printf(">>");
		if(scanf("%d", &choose) != 1)
		{
			while(getchar() != '\n')
				continue;
		}
		else
		{
			switch(choose)
			{
				case 1: case 2:
				case 3: case 4:
				case 5:
					break;
				default:
					continue;
			}
			break;
		}
	}
	
	for(int i = 0; i < 40; i++)
		putchar('/');
	putchar('\n');

	return choose;
}

void print_origin(char (*str)[SIZE])
{
	int i = 0;

	while (i < LENGTH && str[i][0] != '\0')
	{
		puts(str[i]);
		i++;
	}
}

void print_orderby_ascii(char (*str)[SIZE])
{
	// strcmp
	int i = 0;
	char res_str[LENGTH][SIZE];
	char temp[SIZE];

	while(i < LENGTH && str[i][0] != '\0')
	{
		strcpy(res_str[i], str[i]);
		i++;
	}

	i = 0;
	while(i < LENGTH && str[i][0] != '\0')
	{
		for(int j = i + 1; j < LENGTH; j++)
		{
			if(strcmp(res_str[i], res_str[j]) > 0)
			{
				strcpy(temp, res_str[i]);
				strcpy(res_str[i], res_str[j]);
				strcpy(res_str[j], temp);
			}
		}
		i++;
	}
	print_origin(res_str);
}

void print_orderby_length(char (*str)[SIZE])
{
	// strlen
	int i = 0;
	char res_str[LENGTH][SIZE];
	char temp[SIZE];

	while(i < LENGTH && str[i][0] != '\0')
	{
		strcpy(res_str[i], str[i]);
		i++;
	}

	i = 0;
	while(i < LENGTH && str[i][0] != '\0')
	{
		for(int j = i + 1; j < LENGTH; j++)
		{
			if(strlen(res_str[i]) > strlen(res_str[j]))
			{
				strcpy(temp, res_str[i]);
				strcpy(res_str[i], res_str[j]);
				strcpy(res_str[j], temp);
			}
		}
		i++;
	}
	print_origin(res_str);
}

void print_orderby_first_word_length(char (*str)[SIZE])
{
	int i = 0, j = 0;
	char res_str[LENGTH][SIZE];
	char temp[SIZE];

	while(i < LENGTH && str[i][0] != '\0')
	{
		j = 0;
		while( !isspace(str[i][j]) )
		{
			res_str[i][j] = str[i][j];
			j++;
		}
		res_str[i][j-1] = '\0';
		i++;
	}

	i = 0;
	while(i < LENGTH && str[i][0] != '\0')
	{
		for(j = i + 1; j < LENGTH; j++)
		{
			if(strlen(res_str[i]) > strlen(res_str[j]))
			{
				strcpy(temp, res_str[i]);
				strcpy(res_str[i], res_str[j]);
				strcpy(res_str[j], temp);
			}
		}
		i++;
	}
	print_origin(res_str);
}

12

EOF를 만날 때까지 입력을 읽어서 단어 수, 대문자 수, 소문자 수, 구두점 수, 숫자 수를 보고하는 프로그램을 작성하라. ctype.h 계열의 함수들을 사용하라.

#include <stdio.h>
#include <ctype.h>

int main(void)
{
	char ch;
	int word_flag = 0;
	int number_flag = 0;

	int word_cnt = 0;
	int up_cnt = 0;
	int low_cnt = 0;
	int punc_cnt = 0;
	int num_cnt = 0;

	while(1)
	{
		printf(">> ");
		ch = getchar();

		if(ch == EOF)
			break;

		if(isupper(ch)) up_cnt++;
		else if(islower(ch)) low_cnt++;
		else if(ispunct(ch)) punc_cnt++;
		
		if(isspace(ch))
		{
			if(word_flag == 1) word_flag = 0;
			else if(number_flag == 1) number_flag = 0;
		}
		else if(!isspace(ch))
		{
			if(word_flag == 0 && isalpha(ch))
			{
				word_flag = 1;
				word_cnt++;

				if(number_flag == 1)
				{
					number_flag = 0;
					num_cnt--;
				}
			}
			else if(number_flag == 0 && isdigit(ch))
			{
				number_flag = 1;
				num_cnt++;

				if(word_flag == 1)
				{
					word_flag = 0;
					word_cnt--;
				}
			}
		}
	}

	printf("카운트 결과\n");
	printf("단어 수 : %d\n", word_cnt);
	printf("대문자 수 : %d\n", up_cnt);
	printf("소문자 수 : %d\n", low_cnt);
	printf("구두점 수 : %d\n", punc_cnt);
	printf("숫자 수 : %d\n", num_cnt);
}

13

명령행 전달인자들을 역순으로 에코하는 프로그램을 작성하라. 예를 들어, 명령행 전달인자들이 see you later로 주어졌다면, 프로그램은 later you see라고 출력해야 한다.

#include <stdio.h>

int main(int argc, char *argv[])
{
	for(int i = argc - 1; i > 0; i--)
	{
		printf("%s ", argv[i]);
	}
	putchar('\n');

	return 0;
}

14

명령행으로 동작시키는 멱승법(power-law) 프로그램을 적성하라. 명령행의 첫번째 전달인자는 어떤 멱승을 구할 double형 밑수가 되고, 두 번째 전달인자는 정수형 지수가 되어야 한다.

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

int check_number(char *str);
double pow_func(double base, int exponent);

int main(int argc, char *argv[])
{
	double base;
	int exponent;

	if(check_number(argv[1]) == 0)
	{
		puts("밑수가 순수한 수가 아닙니다.");
	}
	else if(check_number(argv[2]) == 0)
	{
		puts("지수가 순수한 수가 아닙니다.");
	}
	else
	{
		base = atof(argv[1]);
		exponent = atoi(argv[2]);
		printf("멱승의 결과는 %.2lf\n", pow_func(base, exponent));
	}

	return 0;
}

int check_number(char *str)
{
	for(int i = 0; str[i] != '\0'; i++)
	{
		if(isdigit(str[i]) == 0)
			return 0;
	}
	return 1;
}

double pow_func(double base, int exponent)
{
	for(int i = 1; i < exponent; i++)
	{
		base *= base;
	}
	return base;
}

15

문자 분류 함수들을 사용하여 atoi()와 동일한 기능을 수행하는 함수를 작성하라. 이 버전의 경우 입력 문자열이 순수한 수가 아니라면 0의 값을 반환 하도록 하라.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define SIZE 10

int atoi_func(char *str);

int main(void)
{
	char str[SIZE];

	while(1)
	{
		puts("종료하려면 0을 입력하세요.");
		printf(">>");
		scanf("%s", str);
		
		if(strcmp(str, "0") == 0)
			break;

		printf("%d\n", atoi_func(str));
	}
}

int atoi_func(char *str)
{
	int num = 0;
	char digits[] = {'0','1','2','3','4','5','6','7','8','9'};

	for(int i = 0; str[i] != '\0'; i++)
	{
		if(isdigit(str[i]) == 0)
			return 0;

		num *= 10;
		for(int j = 0; j < 10; j++)
		{
			if(str[i] == digits[j])
			{
				num += j;
				break;
			}
		}
	}
	return num;
}

16

파일 끝에 도달할 때까지 입력을 읽고, 그것을 디스플레이에 에코하는 프로그램을 작성하라. 프로그램은 다음과 같은 명령행 전달인자들을 인식하고 그 기능을 구현해야 한다.

-p : 입력을 그대로 출력한다.
-u : 입력을 모두 대문자로 변경하여 출력한다.
-l : 입력을 모두 소문자로 변경하여 출력한다.

또한 명령행 전달인자가 없다면, 프로그램이 -p 전달인자와 사용된 것처럼 실행되게 하라.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main(int argc, char *argv[])
{
	char option = 'p';
	char in, out;

	if(argc > 1)
	{
		if(strcmp(argv[1], "-p") == 0)
			option = 'p';
		else if(strcmp(argv[1], "-u") == 0)
			option = 'u';
		else if(strcmp(argv[1], "-l") == 0)
			option = 'l';
	}

	while(1)
	{
		in = getchar();

		if(in == EOF)
			break;

		switch(option)
		{
			case 'p':
				out = in;
				break;
			case 'u':
				out = toupper(in);
				break;
			case 'l':
				out = tolower(in);
				break;
		}

		putchar(out);
	}
	putchar('\n');

	return 0;
}

0개의 댓글