C++Primer第五版中文版 第5章 练习题答案

该博客围绕C++编程基础展开,包含空语句、块的概念及使用场景,还涉及逗号运算符改写循环、条件语句与运算符使用、循环语句应用、错误修正、异常处理等练习解答,帮助读者巩固C++编程基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

练习 5.1:

什么是空语句?什么时候会用到空语句?

答:

  • 空语句由一个单独的分号构成。它实际上什么都不做。当需要在代码中提供一个语句,但又不想执行任何实际操作时,就可以使用空语句。

练习 5.2:

什么是块?什么时候会用到块?

答:

  • 块是指由一对花括号所包围的一段代码。这段代码中可以包含零条或多条语句,甚至可以嵌套包含其他的块。块的作用主要有以下两个方面:
  • 限制作用域:在块中定义的变量,其作用域仅限于该块。当代码执行离开这个块时,这些变量就会被销毁。这对于防止命名冲突以及管理内存使用非常有用。
  • 组织代码:块也可以用来将相关的代码组织在一起,增强代码的可读性和可维护性。

练习5.3:

使用逗号运算符(参见4.10 节,第140页)重写 1.4.1 节(第10页)的 while循环,使它不再需要块,观察改写之后的代码的可读性提高了还是降低了。

答:

while (val <= 10)
    sum += val, ++val;
  • 虽然这样的代码更简短,但是它的可读性可能降低了。一般来说,使用块和新行来组织代码可以使代码更清晰、更易读。

练习 5.4:

说明下列例子的含义,如果存在问题,试着修改它。

	(a) while (string::iterator iter != s.end()) {/*...*/ }
	(b) while(bool status=find(word)){/*...*/}
		    if(!status){/*...*/}

答:

  • (a) 尝试使用 string::iterator 类型的变量 iter,但并没有声明它。应该在循环之前声明和初始化这个变量。修改后的代码可能如下:
	string s = "example";
	string::iterator iter = s.begin();
	while (iter != s.end()) {
		/* ...*/
		++iter;
	}
  • (b) 使用一个函数 find(word) 并将其返回值赋给一个 bool 类型的变量 status。但是,status 是在 while 循环的条件部分声明的,所以在循环体之外是无法访问的。修改后的代码可能如下:
    while(bool status=find(word)){
    	if (!status) {
    	/*...*/
    	}
    }

练习 5.5:

写一段自己的程序,使用 if else 语句实现把数字成绩转换成字母成绩的要求。

答:

#include <iostream>
#include <string>

int main() {
	unsigned grade;
	std::string letterGrade;
	std::cout << "Please enter your numeric grade: ";
	std::cin >> grade;

	if (grade < 60) {
		letterGrade = "F";
	} else if (grade < 70) {
		letterGrade = "D";
	} else if (grade < 80) {
		letterGrade = "C";
	} else if (grade < 90) {
		letterGrade = "B";
	} else if (grade < 100) {
		letterGrade = "A";
	} else {
		letterGrade = "A++";
		grade = 100;
	}

	if (grade >= 60 && grade != 100) {
		int lastDigit = grade % 10;
		if (lastDigit >= 8) {
			letterGrade += "+";
		} else if (lastDigit <= 2) {
			letterGrade += "-";
		}
	}
	std::cout << "Your letter grade is: " << letterGrade << std::endl;

	return 0;
}

练习 5.6:

改写上一题的程序,使用条件运算符(参见4.7节,第134页)代替if else语句。

答:

#include <iostream>
#include <string>

int main() {
	unsigned grade;
	std::string letterGrade;
	std::cout << "Please enter your numeric grade: ";
	std::cin >> grade;

	letterGrade = (grade < 60) ? "F" :
		(grade < 70) ? "D" :
		(grade < 80) ? "C" :
		(grade < 90) ? "B" :
		(grade < 100) ? "A" : "A++";

	letterGrade += (grade < 60 || grade > 100) ? "" :
		(grade % 10 >= 8) ? "+" :
		(grade % 10 <= 2) ? "-" : "";
	std::cout << "Your letter grade is: " << letterGrade << std::endl;

	return 0;
}

练习 5.7:

改正下列代码段中的错误。

	(a) if (ival1 != ival2)
		ival1 = ival2
	    else ival1 = ival2 = 0;
	    
	(b) if (ival < minval)
		minval = ival;
		occurs = 1;
	
	(c) if (int ival = get_value()) 
		cout << "ival = " << ival << endl;
	    if (!ival)
		cout << "ival = 0\n";
		
	(d) if (ival = 0)
		ival = get_value();

答:

  • (a):
	if (ival1 != ival2)
		ival1 = ival2;
	else ival1 = ival2 = 0;
  • (b):
	if (ival < minval) {
		minval = ival;
		occurs = 1;
	}
  • ©:
	if (int ival = get_value())
		cout << "ival = " << ival << endl;
	else if (!ival)
		cout << "ival = 0\n";
  • (d):
	if (ival == 0)
		ival = get_value();

练习 5.8:

什么是“悬垂else” ? C++语言是如何处理else子句的?

答:

  • 因为 if 语句可以嵌套,而且每个 if 语句都可以选择是否包含 else 分支。如果有多个连续的 if 语句并且都没有 else 分支,当最后一个 if 后面出现了一个 else,那么这个 else 应该归属于哪个 if 就是所谓的 “悬垂 else” 问题。
  • C++的规则是:else子句会匹配最近的尚未匹配的if。

练习 5.9:

编写一段程序,使用一系列 if 语句统计从 cin 读入的文本中有多少元音字母。

答:

#include <iostream>
#include <cctype>

using std::cout;

int main() {
	unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
	char ch;
	while (std::cin >> ch) {
		ch = std::tolower(ch);	//将大写字母转换为小写
		//如果ch是元音字母,将其对应的计数值加1
		if (ch == 'a') {
			++aCnt;
		} else if (ch == 'e') {
			++eCnt;
		} else if (ch == 'i') {
			++iCnt;
		} else if (ch == 'o') {
			++oCnt;
		} else if (ch == 'u') {
			++uCnt;
		}
	}
	//输出结果
	cout << "Number of vowel a: \t" << aCnt << '\n'
		<< "Number of vowel e: \t" << eCnt << '\n'
		<< "Number of vowel i: \t" << iCnt << '\n'
		<< "Number of vowel o: \t" << oCnt << '\n'
		<< "Number of vowel u: \t" << uCnt << std::endl;

	return 0;
}

练习 5.10:

我们之前实现的统计元音字母的程序存在一个问题:如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小写形式,也统计大写形式,也就是说,新程序遇到’a’和’A’都应该递增 aCnt 的值,以此类推。

答:

#include <iostream>

using std::cout;

int main() {
	unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
	char ch;
	while (std::cin >> ch) {
		//如果ch是元音字母,将其对应的计数值加1
		switch (ch) {
		case 'a': case 'A':
			++aCnt;
			break;
		case 'e': case 'E':
			++eCnt;
			break;
		case 'i': case 'I':
			++iCnt;
			break;
		case 'o': case 'O':
			++oCnt;
			break;
		case 'u': case 'U':
			++uCnt;
			break;
		default:
			break;
		}
	}
	//输出结果
	cout << "Number of vowel a: \t" << aCnt << '\n'
		<< "Number of vowel e: \t" << eCnt << '\n'
		<< "Number of vowel i: \t" << iCnt << '\n'
		<< "Number of vowel o: \t" << oCnt << '\n'
		<< "Number of vowel u: \t" << uCnt << std::endl;

	return 0;
}

练习 5.11:

修改统计元音字母的程序,使其也能统计空格、制表符和换行符的数量。

答:

#include <iostream>

using std::cout;

int main() {
	unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
	unsigned spaceCnt = 0, tabCnt = 0, nlCnt = 0;
	char ch;
	while (std::cin.get(ch)) {
			//如果ch是元音字母或空白字符,将其对应的计数值加1
			switch (ch) {
			case 'a': case 'A':
				++aCnt;
				break;
			case 'e': case 'E':
				++eCnt;
				break;
			case 'i': case 'I':
				++iCnt;
				break;
			case 'o': case 'O':
				++oCnt;
				break;
			case 'u': case 'U':
				++uCnt;
				break;
			case' ':
				++spaceCnt;
				break;
			case'\t':
				++tabCnt;
				break;
			case'\n':
				++nlCnt;
				break;
			default:
				break;
			}
		}
	//输出结果
	cout << "Number of vowel a: \t" << aCnt << '\n'
		<< "Number of vowel e: \t" << eCnt << '\n'
		<< "Number of vowel i: \t" << iCnt << '\n'
		<< "Number of vowel o: \t" << oCnt << '\n'
		<< "Number of vowel u: \t" << uCnt << '\n'
		<< "Number of space: \t" << spaceCnt << '\n'
		<< "Number of tab: \t" << tabCnt << '\n'
		<< "Number of new line: \t" << nlCnt << std::endl;

	return 0;
}

练习 5.12:

修改统计元音字母的程序,使其能统计以下含有两个字符的字符序列的数量:ff、fl 和 fi。

答:

#include <iostream>

using std::cout;

int main() {
	unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
	unsigned ffCnt = 0, flCnt = 0, fiCnt = 0;
	char ch, prev_ch = '\0';	// prev_ch用于存储上一个字符
	while ((ch = std::cin.get()) != EOF) {
		//如果ch是元音字母,将其对应的计数值加1
		switch (ch) {
		case 'a': case 'A':
			++aCnt;
			break;
		case 'e': case 'E':
			++eCnt;
			break;
		case 'i':
			if (prev_ch == 'f')++fiCnt;	// 如果上一个字符是'f', 增加'fi'的计数
		case 'I':
			++iCnt;
			break;
		case 'o': case 'O':
			++oCnt;
			break;
		case 'u': case 'U':
			++uCnt;
			break;
		case 'f':
			if (prev_ch == 'f')++ffCnt;	// 如果上一个字符是'f', 增加'ff'的计数
			break;
		case 'l':
			if (prev_ch == 'f')++flCnt;	// 如果上一个字符是'f', 增加'fl'的计数
			break;
		default:
			break;
		}
		prev_ch = ch;	// 更新prev_ch以存储当前字符
	}
	//输出结果
	cout << "Number of vowel a: \t" << aCnt << '\n'
		<< "Number of vowel e: \t" << eCnt << '\n'
		<< "Number of vowel i: \t" << iCnt << '\n'
		<< "Number of vowel o: \t" << oCnt << '\n'
		<< "Number of vowel u: \t" << uCnt << '\n'
		<< "Number of sequence ff: \t" << ffCnt << '\n'
		<< "Number of sequence fl: \t" << flCnt << '\n'
		<< "Number of sequence fi: \t" << fiCnt << std::endl;

	return 0;
}

练习5.13:

下面显示的每个程序都含有一个常见的编程错误,指出错误在哪里,然后修改它们。

(a) 
	unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
	char ch = next_text();
	switch (ch) {
	case 'a': aCnt++;
	case 'e': eCnt++;
	default: iouCnt++;
	}	    
(b) 
	unsigned index=some_value();
	switch (index) {
	case 1: 
		int ix = get_value();
		ivec[ix] = index;
		break;
	default:
		ix = ivec.size() - 1;
		ivec[ix] = index;
	}
(c)
	unsigned evenCnt = 0, oddCnt = 0;
	int digit = get_num() % 10;
	switch (digit) {
	case 1, 3, 5, 7, 9:
		oddCnt++;
		break;
	case 2, 4, 6, 8, 10:
		evenCnt++;
		break;
	}
(d)
	unsigned ival = 512, jval = 1024, kval = 4096;
	unsigned bufsize;
	unsigned swt = get_bufCnt();
	switch (swt) {
	case ival:
		bufsize = ival * sizeof(int);
		break;
	case jval:
		bufsize = jval * sizeof(int);
		break;
	case kval:
		bufsize = kval * sizeof(int);
		break;
	}

答:

//(a) 的问题是switch语句中没有使用break语句,导致一旦满足某个case,后面的case和default都会执行。
	unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
	char ch = next_text();
	switch (ch) {
	case 'a':
		aCnt++;
		break;
	case 'e':
		eCnt++;
		break;
	default: 
		iouCnt++;
		break;
	}
//(b) 的问题在于ix只在第一个case中声明和定义,因此在default中是无法访问的。
	unsigned index = some_value();
	int ix;
	switch (index) {
	case 1:
		ix = get_value();
		ivec[ix] = index;
		break;
	default:
		ix = ivec.size() - 1;
		ivec[ix] = index;
	}
//(c) 的问题是case的值并不能像这样使用逗号分隔,它需要使用多个case语句。
	unsigned evenCnt = 0, oddCnt = 0;
	int digit = get_num() % 10;
	switch (digit) {
	case 1: case 3: case 5: case 7: case 9:
		oddCnt++;
		break;
	case 2: case 4: case 6: case 8: case 0: // 这里是0而不是10,因为digit只能是0-9的整数
		evenCnt++;
		break;
	}
//(d) 的问题在于case语句的表达式必须是常量表达式,而不是变量。
	constexpr unsigned ival = 512, jval = 1024, kval = 4096; // 使用constexpr定义常量表达式
	unsigned bufsize;
	unsigned swt = get_bufCnt();
	switch (swt) {
	case ival:
		bufsize = ival * sizeof(int);
		break;
	case jval:
		bufsize = jval * sizeof(int);
		break;
	case kval:
		bufsize = kval * sizeof(int);
		break;
	}

练习 5.14:

编写一段程序,从标准输入中读取若干 string 对象并查找连续重复出现的单词。所谓连续重复出现的意思是:一个单词后面紧跟着这个单词本身。要求记录连续重复出现的最大次数以及对应的单词。如果这样的单词存在,输出重复出现的最大次数;如果不存在,输出一条信息说明任何单词都没有连续出现过。例如,如果输入是how now now now brown cow cow那么输出应该表明单词now连续出现了3次。

答:

#include <iostream>
#include <string>

int main() {
	std::string word = "", preWord = "", maxWord = "";
	int count = 0, maxCount = 0;

	while (std::cin >> word) {
		if (word == preWord) {
			++count;
			if (count > maxCount) {
				maxWord = word;
				maxCount = count;
			}
		} else {
			preWord = word;
			count = 1;
		}
	}

	if (maxCount > 1) {
		std::cout << "The word " << maxWord << " occured " << maxCount << " times consecutively." << std::endl;
	} else {
		std::cout << "No word was occured consecutively." << std::endl;
	}

	return 0;
}

练习 5.15:

说明下列循环的含义并改正其中的错误。

(a)
	for (int ix = 0; ix != sz; ++ix){/* ...*/}
	if(ix!=sz)
		// ...
(b)
	int ix;
	for (ix != sz; ++ix) {/* ...*/ }
(c)
	for (int ix = 0; ix != sz; ++ix, ++sz) {/* ...*/ }

答:

//(a) 变量 ix 在 for 循环之外是不可见的,因此 if(ix!=sz) 将无法通过编译。如果需要在 for 循环之外使用 ix,那么应该在 for 循环外部声明这个变量
	int ix;
	for (ix = 0; ix != sz;++ix){/* ...*/}
	if (ix != sz) {
		// ...
	}
//(b) for 循环的初始化部分并未初始化 ix,那么条件语句左侧需要一个空语句
	int ix = 0;
	for (; ix != sz; ++ix) {/* ...*/ }
//(c) ix 和 sz 同时增加,那么 ix 永远不可能等于 sz,这将会导致无限循环。
	for (int ix = 0; ix != sz; ++ix) {/* ...*/ }

练习 5.16:

while循环特别适用于那种条件保持不变、反复执行操作的情况,例如,当未达到文件末尾时不断读取下一个值。for循环则更像是在按步骤迭代,它的索引值,在某个范围内依次变化。根据每种循环的习惯用法各自编写一段程序,然后分别用另一种循环改写。如果只能使用一种循环,你倾向于使用哪种呢?为什么?

答:

	//原始while循环
	int num;
	while (cin >> num) {
		// ...
	}
	//转化为for循环:
	for (int num; cin >> num;) {
		// ...
	}
	
	//原始for循环:
	for (int i = 0; i < 10; ++i) {
		cout << i << " ";
	}
	//转化为while循环:
	int i = 0;
	while (i < 10) {
		cout << i << " ";
		++i;
	}
  • 如果只能使用一种循环,我选择for循环,因为for循环在大多数情况下可以替代while循环,但反之并非如此。如果能选两种,我首选while。好看。

练习 5.17:

假设有两个包含整数的 vector 对象,编写一段程序,检验其中一个 vector对象是否是另一个的前缀。为了实现这一目标,对于两个不等长的 vector 对象,只需•挑出长度较短的那个,把它的所有元素和另一个vector对象比较即可。例如,如果两个vector对象的元素分别是0、1、1、2和0、1、1、2、3、5、8,则程序的返回结果应该为真。

答:

#include <iostream>
#include <vector>

int main() {
	std::vector<int> vec_1 = { 0, 1, 1, 2 };
	std::vector<int> vec_2 = { 0, 1, 1, 2, 3, 5, 8 };
	bool is_prefix = true;

	// 取得长度较短的那个vector的长度
	auto len = vec_1.size() < vec_2.size() ? vec_1.size() : vec_2.size();

	// 遍历较短的vector的每一个元素,和较长的vector做比较
	for (int i = 0; i < len; ++i)
	{
		if (vec_1[i] != vec_2[i]) {
			is_prefix = false;
		}
	}

	if (is_prefix) {
		std::cout << "vec1 is a prefix of vec2\n";
	} else {
		std::cout << "vec1 is not a prefix of vec2\n";
	}

	return 0;
}

练习 5.18:

说明下列循环的含义并改正其中的错误。

(a)
	do
		int v1, v2;
		cout << "Please enter two numbers to sum:";
		if (cin >> v1 >> v2)
			cout << "Sum is: " << v1 + v2 << endl;
	while (cin);
(b)
	do {
		//...
	} while (int ival = get_response());
(c)
	do {
		int ival = get_response();
	} while (ival);

答:

//(a) 这个 do-while 循环不包含花括号,正确的做法是将循环体放在花括号中。
	do {
		int v1, v2;
		cout << "Please enter two numbers to sum:";
		if (cin >> v1 >> v2)
			cout << "Sum is: " << v1 + v2 << endl;
	} while (cin);
//(b) do-while 循环条件中使用的变量必须定义在循环体外。
	int ival;
	do {
		//...
	} while (ival = get_response());
//(c) 变量 ival 在 do 语句块中被声明,所以它的作用范围只在 do 语句块中,while 不能访问它。
	int ival;
	do {
		ival = get_response();
	} while (ival);

练习 5.19:

编写一段程序,使用do while循环重复地执行下述任务:首先提示用户输入两个string对象,然后挑出较短的那个并输出它。

答:

#include <iostream>
#include <string>

int main() {
	do {
		std::string s1, s2;
		std::cout << "Please enter two strings: ";
		std::cin >> s1 >> s2;
		std::cout << "The shorter one is: " << (s1 <= s2 ? s1 : s2) << std::endl;
		std::cout << "Would you like to compare another pair of strings? (y/n): ";
		char c;
		std::cin >> c;
		if (c != 'y' && c != 'Y') {
			break;
		}
	} while (true);
	
	return 0;
}

练习 5.20:

编写一段程序,从标准输入中读取 string 对象的序列直到连续出现两个相同的单词或者所有单词都读完为止。使用 while 循环一次读取一个单词,当一个单词连续出现两次时使用 break 语句终止循环。输出连续重复出现的单词,或者输出一个消息说明没有任何单词是连续重复出现的。

答:

#include <iostream>
#include <string>

int main() {
	std::string prevWord = "", currWord = "";
	bool isRepeated = false;

	while (std::cin >> currWord) {
		if (prevWord == currWord) {
			isRepeated = true;
			break;
		}
		prevWord = currWord;
	}

	if (isRepeated) {
		std::cout << "The word '" << currWord << "' was repeated." << std::endl;
	} else {
		std::cout << "No word was repeated." << std::endl;
	}

	return 0;
}

练习 5.21:

修改 5.5.1 节(第 171 页)练习题的程序,使其找到的重复单词必须以大写字母开头。

答:

#include <iostream>
#include <string>
#include <cctype>

int main() {
	std::string prevWord = "", currWord = "";
	bool isRepeated = false;

	while (std::cin >> currWord) {
		if (!std::isupper(currWord[0])) {
			prevWord = currWord;
			continue;
		} else if (prevWord == currWord) {
			isRepeated = true;
			break;
		}
		prevWord = currWord;
	}

	if (isRepeated) {
		std::cout << "The word '" << currWord << "' was repeated." << std::endl;
	} else {
		std::cout << "No word was repeated." << std::endl;
	}

	return 0;
}

练习 5.22:

本节的最后一个例子跳回到 begin,其实使用循环能更好地完成该任务。重写这段代码,注意不再使用 goto 语句。

begin:
	int sz = get_size();
	if (sz <= 0) {
		goto begin;
	}

答:

	int sz;
	do {
		sz = get_size();
	} while (sz <= 0);
  • 这段代码并没能模拟goto例子的全部效果,使用标准循环结构时,难以做到在每轮循环中都重新初始化及销毁sz的同时还不影响sz的作用域。

练习 5.23:

编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果。

答:

#include <iostream>

int main() {
	int num_1, num_2;
	while (true) {
		std::cout << "Please enter two integers: " << std::endl;
		std::cin >> num_1 >> num_2;

		double result = static_cast<double>(num_1) / num_2;
		std::cout << "The result of " << num_1 << " divided by " << num_2 << " is " << result << std::endl;
		break;	//跳出循环
	}

	return 0;
}

练习 5.24:

修改你的程序,使得当第二个数是 0 时抛出异常。先不要设定 catch 子句,运行程序并真的为除数输入 0,看看会发生什么?

答:

#include <iostream>
#include <stdexcept>

int main() {
	int num_1, num_2;
	while (true) {
		std::cout << "Please enter two integers: " << std::endl;
		std::cin >> num_1 >> num_2;
		if (num_2 == 0) {
			throw std::invalid_argument("The second number cannot be zero!");
		}
		double result = static_cast<double>(num_1) / num_2;
		std::cout << "The result of " << num_1 << " divided by " << num_2 << " is " << result << std::endl;
		break;	//没有抛出异常则跳出循环
	}

	return 0;
}
  • 除数为0时,程序会停止工作。

练习 5.25:

修改上一题的程序,使用try 语句块去捕获异常。catch子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行try 语句块的内容。

答:

#include <iostream>
#include <stdexcept>

int main() {
	while (true) {	
		try {
			std::cout << "Please enter two integers: " << std::endl;
			int num_1, num_2;
			std::cin >> num_1 >> num_2;
			if (num_2 == 0) {
				throw std::invalid_argument("The second number cannot be zero!");
			}
			double result = static_cast<double>(num_1) / num_2;
			std::cout << "The result of " << num_1 << " divided by " << num_2 << " is " << result << std::endl;
			break;	//没有抛出异常则跳出循环
		}
		catch (const std::invalid_argument &e) {
			std::cout << "Exception: " << e.what() << std::endl;
			std::cout << "Would you like to enter two new numbers? (y/n)" << std::endl;
			char c;
			std::cin >> c;
			if (c != 'y') {
				break;
			}
		}
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值