《C Primer Plus》第 8 章复习题和编程练习

一、复习题

1. putchar(getchar()) 是一个有效的表达式,它要实现什么功能?getchar(putchar()) 是否也是一个有效的表达式?

答:前者回显输入的字符,因为 getchar() 获取输入的字符并返回,putchar() 显示参数字符。后者不是一个有效的表达式,putchar() 函数需要参数,而 getchar() 函数不需要参数。

2. 下面的语句分别完成什么任务?
a. putchar(‘H’)
b. putchar(‘\007’)
c. putchar(‘\n)
d. putchar(’\b’)

答:
a. 打印字符 ‘H’
b. ‘\007’ 对应转义字符 ‘\a’ ,表示警报
c. 打印换行符
d. 打印一个退格符

3. 假设有一个名为 count 的可执行程序,用于统计输入的字符数。设计一个命令行,该命令行使用 count 程序统计 easy 文件中的字符数,并把统计结果保存在 essayct 文件中。

答:因为命令与重定向的顺序无关,所有有两种方法:count < easy > essayct 或者 count > essayct < easy。

4. 根据复习题 3 中的程序和文件,下面那一条是有效命令?
a. essayct < essay;
b. count essay;
c. essay > count;

答:
a. 无效命令,重定向运算符只能用于链接一个可执行程序和一个数据文件。
b. 无效命令,没有重定向运算符。
c. 无效命令,可执行程序何数据文件的顺序颠倒。在很多操作系统中,系统会认为 essay 是可执行程序,而 count 是数据文件。

5. EOF 是什么?

答:EOF 是 C 语言中表示文件结尾的特殊数值,当使用 getchar() 和 scanf() 函数读取文件检测到文件结尾时返回 EOF。EOF 是在 stdio.h 文件中利用预编译指令定义的。
#define EOF (-1)

**6. 对于给定的输出(ch 是 int 类型,而且是缓冲输入),下面各程序段的输出分别是什么?
a. 输入如下。
If you quit, I will.[enter]
程序段如下。
while ((ch = getchar()) != ‘i’)
  putchar(ch);
b. 输入如下。
Harhar[enter]
程序段如下。
while ((ch = getchar()) != ‘\n’)
{
  putchar(ch++);
  putchar(++ch);
}
**

答:
a. 程序段输出如下:If you qu
b. 程序段输出如下:HJacrthjacrt

7. C 如何处理不同计算机系统中不同文件和换行约定?

答:文件本质上就是存储在计算机存储器中的信息区域,不同的系统存储文件的形式不同,这和系统的软硬件底层技术相关。而文件的结构也会因系统处理文件的差异有所不同,例如,换行符和回车符作为段落换行标记在很多系统上并不是统一的。为了解决文件存储形式上的差异和文件结构上的差异给程序设计带来的难题,C 语言应用预定义的标准 I/O 库形式,将不同属性和不同种类的输入作为统一的流进行处理。标准 I/O 库隐藏了文件原有的差异,更加有利于程序员的编程和处理。

8. 在使用缓冲输入的系统中,混合输入数值和字符会遇到什么潜在的问题?

答:scanf() 即可以读取字符也可以读取数字,但是会跳过第一个非空白字符之前的空白。而 char() 只能读取字符,且不会跳过前面的空白。通常情况下,使用 scanf() 函数读取数值,而使用 getchar() 函数读取字符。但是,scanf() 函数读取完数值后,会把换行符(‘\n’)留在输入流中,需要注意并清理换行符。

二、 编程练习

1. 设计一个程序,统计在读到文件结尾之前读取的字符数。

答:
程序设计分析: 循环条件使用 EOF 检测文件结尾。

代码如下:

#include <stdio.h>

int main()
{
	// 统计输入的字符数,直到检测到文件结尾
	int num = 0;
	int input = 0;

	// 输入
	printf("请输入一段字符:");
	while ((input = getchar()) != EOF)
	{
		++num;
	}

	// 输出
	printf("输入的字符数为:%d\n", num);

	return 0;
}

2. 编写一个程序,在遇到 EOF 之前,作为字符流读取输入。程序要打印每个输入的字符机器相应的 ASCII 十进制。注意,在 ASCII 序列中,空格前面的字符都是非打印字符,要特殊处理这些字符。如果非打印字符时换行符或制表符,分别打印\n或\t;否则,使用控制字符表示法。例如,在 ASCII 码中,1 表示的是 Ctrl + A,可显示为 ^A。注意,A 的 ASCII 值是 Ctrl + A 的值加上 64。对于其他非打印字符,也有类似关系。除每次遇到换行符打印新的 1 行外,每行打印 10 对值。(注意,不同操作系统的控制字符可能不同。)

答:
程序设计分析: 首先,要记录读取的字符个数,每 10 个换一行。其次,使用 if else 来对输入的字符进行分类处理。

代码如下:

#include <stdio.h>

int main()
{	
	// 所需变量
	int input = 0;
	int n_cur_line = 0;

	// 输入
	while ((input = getchar()) != EOF)
	{
		// 判断是否换行
		if (n_cur_line++ == 10)
		{
			printf("\n");
			n_cur_line = 1;
		}

		// 判断输入的字符
		if (input >= ' ')
		{
			printf("%c-%d ", input, input);
		}
		else if (input == '\n')
		{
			printf("\\n - \\n\n");
			n_cur_line = 0;
		}
		else if (input = '\t')
		{
			printf("\\t-\\t");
		}
		else
		{
			printf("\'%c\'-^%c", input, input + 64);
		}
	}

	return 0;
}

3. 编写一个程序,在遇到 EOF 之前,作为字符流读取输入。该程序要报告输入中大写字符和小写字母的个数。假设大小写字母的数值是连续的。或者使用 ctype.h 库中合适的分类函数更方便。

答:
程序设计分析: 使用 ctype.h 库中的 isupper() 和 islower() 函数判断并计数。

代码如下:

#include <stdio.h>
#include <ctype.h>  // 字符处理函数

int main()
{
	// 所需变量
	int ch = 0;
	int n_upper = 0;
	int n_lower = 0;

	// 输入
	while ((ch = getchar()) != EOF)
	{
		if (isupper(ch))
			++n_upper;
		else if (islower(ch))
			++n_lower;
	}

	// 输出
	printf("大写字母的个数:%d\n", n_upper);
	printf("小写字母的个数:%d\n", n_lower);

	return 0;
}

4. 编写一个程序,在遇到 EOF 之前,作为字符流读取输入。该程序要报告平均每个单词的字母数,不要把空白字符统计在内。实际上,标点符号也不应该统计,但是现在不用考虑这么多。(如果你比较在意这一点,考虑使用 ctype.h 库中的 ispunck() 函数。)

答:
程序设计分析: 下面的代码不计算标点符号,且使用 ctype.h 中的函数。需要使用一个标记来验证是否是单词。

代码如下:

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

int main()
{
	// 所需变量
	int ch = 0;
	int n_word = 0;
	int n_char = 0;
	int is_word = 0;

	// 输入
	while ((ch = getchar()) != EOF)
	{
		if (isspace(ch) && is_word)
		{
			++n_word;
			is_word = 0;
		}
		else if (!ispunct(ch))
		{
			++n_char;
			is_word = 1;
		}
	}

	// 输出
	printf("平均单词的字母数为:%lf\n", (double)n_char / n_word);

	return 0;
}

5. 修改程序清单 8.4 中的猜数字程序,使用更加智能的猜测策略。例如,程序最初猜测 50,询问用户是猜大了还是猜小了还是猜对了。如果猜小了,那么下一次猜测的值应是 50 和 100 的中值,也就是 75 。如果这次猜大了,那么下一次应该是 50 和 75 的中值,等等。使用二分查找策略,如果用户没有欺骗程序,那么程序很快就会猜到正确答案。

答:
程序设计分析: 使用循环和三个变量记录上下限值和中值,其间通过用户输入的指令对范围进行缩减。还要记得处理遗留在输入流中的换行符。

代码如下:

#include <stdio.h>

int main()
{
	// 所需变量
	char ch = 0;
	int upper = 100;
	int lower = 0;
	int middle = 0;

	// 开始游戏
	printf("请在 1 - 100 之间想一个数字,然后计算机来进行猜测,你需要回答 b(猜大了)、s(猜小了)和y(猜对了)\n");
	do
	{
		middle = (upper + lower) / 2;
		printf("是 %d 吗?\n", middle);
		scanf("%c", &ch);

		// 清理换行符
		while (getchar() != '\n')
			continue;

		if (ch == 'b')
		{
			upper = middle;
		}
		else if (ch == 's')
		{
			lower = middle;
		}
		else if (ch == 'y')
		{
			printf("答对了,我厉害吧!\n");
		}
	} while (ch != 'y');

	return 0;
}

6. 修改程序清单 8.8 中的 get_first() 函数,让函数返回读取的第一个非空白字符,并在一个简单的程序中测试。

答:
程序设计分析: 如果为空白则跳过,遇到第一个非空白字符再返回。

代码如下:

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

// 函数声明
char get_first();

int main()
{
	char ch = 0;
	
	ch = get_first();

	printf("%c\n", ch);

	return 0;
}

// 函数定义
char get_first()
{
	char key = 0;

	do
	{
		key = getchar();
		if (!isspace(key))
			return key;
	} while (1);
}

7. 修改第 7 章的编程练习 8,用字符代替数字来标记菜单选项。用 q 代替 5 作为结束的标记。

答:
程序设计分析: 只需要修改菜单的选项,和 switch 中相应的操作即可。

代码如下:

#include <stdio.h>

// 常量声明
#define EXTRA_HOUR 1.5
#define BASE_TAX 0.15
#define EXTRA_TAX 0.20
#define REMAIN_TAX 0.25


// 函数声明
void menu();  // 打印菜单

int main()
{

	// 所需变量
	double hours = 0, sum = 0, tax = 0, income = 0;

	// 显示菜单
	menu();

	// 选择工资档位
	double BASE_SALARY = 0;
	int select = 0;
	printf("请选择你的工资档位:");
	select = getchar();
	while (getchar() != '\n')
		continue;

	switch (select)
	{
	case 'a':
		BASE_SALARY = 8.75;
		break;
	case 'b':
		BASE_SALARY = 9.33;
		break;
	case 'c':
		BASE_SALARY = 10.00;
		break;
	case 'd':
		BASE_SALARY = 11.20;
		break;
	case 'q' :
		break;
	default:
		printf("选择错误!\n");
	}

	//输入一周的工作时长
	printf("Please enter the number of the working hours per week : ");
	scanf("%lf", &hours);

	// 计算总收入
	if (hours > 40)
		hours += (hours - 40) * 0.5;

	sum = hours * BASE_SALARY;

	// 计算税金
	if (sum <= 300)
		tax = sum * BASE_TAX;
	else if (sum <= 450)
		tax = 300 * BASE_TAX + (sum - 300) * EXTRA_TAX;
	else
		tax = 300 * BASE_TAX + 150 * EXTRA_TAX + (sum - 450) * REMAIN_TAX;

	// 计算净收入
	income = sum - tax;

	// 输出
	printf("工资总额:%lf\n", sum);
	printf("税金:%lf\n", tax);
	printf("净收入:%lf\n", income);

	return 0;
}

// 函数定义
void menu()  // 打印菜单
{
	printf("**********************************************************************\n");
	printf("Enter the number corresponding to the desired pay rate or action\n");
	printf("a)$8.75/hr                             b)$9.33/hr\n");
	printf("c)$10.00/hr                            d)$11.20/hr\n");
	printf("q)Quit\n");
	printf("**********************************************************************\n");
}

8. 编写一个程序,显示一个提供加法、减法、乘法、除法的菜单,获得用户选择的菜单选项后,程序提示用户输出两个数字,然后执行用户刚才选择的操作。该程序只接受菜单提供的选项。程序使用 float 类型的变量存储用户输入的数字,如果用户输入失败,则允许再次输入。在进行除法运算时,如果用户输入 0 作为第 2 个数(除数),程序应提示用户再输入一个新值。该程序的一个运行提示如下。
Enter the operation of your choice:
a. add      s. subtract
m. multiply  d. divide
q. quit
a
Enter first number: 22.4
Enter second number: one
one is not an number.
Please enter a number, such as 2.5,-1,78E8,or 3: 1
22.4 + 1 = 23.4
Enter the operation of your choice:
a. add      s. subtract
m. multiply  d. divide
q. quit
d
Enter first number: 18.4
Enter second number: 0
Enter a number other than 0: 0.2
18.4 / 0.2 = 92
Enter the operation of your choice:
a. add      s. subtract
m. multiply  d. divide
q. quit
q
Bye!

答:
程序设计分析: 使用 switch 对每个选项进行分类,混合输入字符和数字注意遗留的换行符。

代码如下:

#include <stdio.h>

// 函数声明
void menu();
float get_num();

int main()
{
	// 所需变量
	int select = 0;
	float n1 = 0;
	float n2 = 0;

	do
	{
		// 输入选项
		menu();
		select = getchar();
		while (getchar() != '\n')
			continue;

		// 处理选项
		switch (select)
		{
		case 'a':
			printf("Enter first number: ");
			n1 = get_num();
			printf("Enter second number: ");
			n2 = get_num();
			printf("%g + %g = %f\n", n1, n2, n1 + n2);
			break;
		case 's':
			printf("Enter first number: ");
			n1 = get_num();
			printf("Enter second number: ");
			n2 = get_num();
			printf("%g - %g = %g\n", n1, n2, n1 - n2);
			break;
		case 'm':
			printf("Enter first number: ");
			n1 = get_num();
			printf("Enter second number: ");
			n2 = get_num();
			printf("%g * %g = %f\n", n1, n2, n1 * n2);
			break;
		case 'd':
			printf("Enter first number: ");
			n1 = get_num();
			printf("Enter second number: ");
			while ((n2 = get_num()) == 0)
			{
				printf("Enter a number other than 0: ");
			}
			printf("%g / %g = %f\n", n1, n2, n1 / n2);
			break;
		case 'q':
			break;
		}
		// 处理换行符
		while (getchar() != '\n')
			continue;
	} while (select != 'q');


	return 0;
}

// 函数定义
void menu()
{
	printf("Enter the operation of your choice:\n");
	printf("a. add               s. subtract\n");
	printf("m. multiply          d. divide\n");
	printf("q. quit\n");
}

float get_num()
{
	float num = 0;
	int ch = 0;

	while (scanf("%f", &num) != 1)
	{
		// 处理多余输入
		while ((ch = getchar()) != '\n')
			putchar(ch);
		// 重新输入
		printf(" is not an number.\nPlease enter a number,"
			" such as 2.5,-1,78E8,or 3: ");
	}

	return num;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值