C/C++教程

《C++Primer(第五版)》第六章课后习题答案

本文主要是介绍《C++Primer(第五版)》第六章课后习题答案,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

6.1节练习

6.1

实参和形参的区别是什么?

6.2

请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?

(a)int f(){
string s;
//...
return s;
}

A:错误,s是string,无法成为int类型的返回变量。修改:string s; -> int s=0;

(b)f2(int i){/*...*/}

A:错误,没有设定返回类型。void f2(int i){/*...*/}

(c)int calc(int v1,int v1)/*...*/}

A:形参名不能相同;代码块没有用{}装起来【别人的答案】。没有返回值。修改:void calc(int v1,int v2){/*...*/}

(d)double square(double x) return x*x;

A:函数体无{}【别人的答案】。修改:double square(double x){ return x*x};

6.3

编写你自己的fact函数,上机检查是否正确。 

int fact(int num) {//求数的阶乘
	int sum = 1;
	if (num > 1) {//计算阶乘
		for (int i = 1; i <= num; ++i) {
			sum *= i;
		}
	}
	else if (num == 0) {//0的阶乘是1
		sum = 1;
	}
	else {//负数没有阶乘
		sum = 0;
	}
	return sum;//返回阶乘
}

6.4 

编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数。

A:调用6.3的fact函数

#include <iostream>
int main() {
	std::cout << "输入一个数,然后你就能得到这个数的阶乘是多少:" << std::endl;
	int a, sum = 1;//a是用来记录用户输入的数据的,sum是用来记录阶乘的
	while (std::cin >> a) {
		sum = fact(a);//运行函数
		if (sum > 0) {
			std::cout << "你输入的数的阶乘为:" << sum << std::endl;
			break;//跳出循环
		}
		else {
			std::cout << "抱歉,你输入的数据无法得到阶乘,请再输一遍。" << std::endl;
		}
	}
	return 0;
}

6.5

编写一个函数输出其实参的绝对值。

#include <iostream>
double GetAbs(double num) {//绝对值函数
	if (num < 0) {//如果数是负数,取负值
		num = -num;
	}
	return num;
}
int main() {
	std::cout << "输入一个数,然后你就能得到这个数的绝对值是多少:" << std::endl;
	double a;
	std::cin >> a;
	std::cout << "这数的绝对值为:" << GetAbs(a)<< std::endl;
	return 0;
}

6.1.1节练习

6.6

说明形参、局部变量以及局部静态变量的区别。

A:形参及函数体内定义的变量,都是局部变量,必须进行初始化,否则会出现未定义行为,这是由于局部变量的生命周期只在函数体内,所以当函数执行结束,其生命周期也就结束了,而局部静态变量,当程序执行路径第一次到达变量的定义对其进行初始化,此时该变量的生命周期直到程序结束才结束,不会由于函数的结束而结束。

6.7

编写一个程序,当它第一次被调用返回0,以后每次被调用返回值加1。

#include <iostream>
int call() {
	static int a = 0;
	++a;
	return a;
}
int main() {
	for (int i = 0; i <= 10; ++i) {
		std::cout <<call()<< std::endl;
	}
	return 0;
}

6.1.2节练习

6.8

编写一个名为Chapter6.h的头文件,令其包含6.1节练习(第184页)中的函数声明。

#ifndef _CHAPER6_H_
#define _CHAPER6_H_
int fact(int val);
void swap(int *p, int *q);
#endif

6.1.3节练习 

6.9

编写你自己的fact.cc 和factMain.cc ,这两个文件都应该包含上一小节的练习中编写的 Chapter6.h 头文件。通过这些文件,理解你的编译器是如何支持分离式编译的。

#include "Chapter6.h"
//求数的阶乘
int fact(int num) {
	int sum = 1;
	if (num > 1) {//计算阶乘
		for (int i = 1; i <= num; ++i) {
			sum *= i;
		}
	}
	else if (num == 0) {//0的阶乘是1
		sum = 1;
	}
	else {//负数没有阶乘
		sum = 0;
	}
	return sum;//返回阶乘
}
#include <iostream>
#include "Chapter6.h"
int main() {
	std::cout << "输入一个数,然后你就能得到这个数的阶乘是多少:" << std::endl;
	int a, sum = 1;//a是用来记录用户输入的数据的,sum是用来记录阶乘的
	while (std::cin >> a) {
		sum = fact(a);//运行函数
		if (sum > 0) {
			std::cout << "你输入的数的阶乘为:" << sum << std::endl;
			break;//跳出循环
		}
		else {
			std::cout << "抱歉,你输入的数据无法得到阶乘,请再输一遍。" << std::endl;
		}
	}
	return 0;
}

6.2.1节练习

6.10

编写一个函数,使用指针形参交换两个整数的值。在代码中调用该函数并输出交换后的结果,以此验证函数的正确性。

#include "Chapter6.h"
void swap(int *p, int *q) {//指针交换形参
	int temp;//临时变量
	temp = *p;//temp获得p指向的地址中的值
	*p =*q;//p指向的地址中的值换成q指向的地址中的值
	*q = temp;//q指向的地址中的值换成temp
}
#include <iostream>
#include "Chapter6.h"
int main() {
	int a, b, *p, *q;
	a = 1;
	b = 5;
	p = &a;
	q = &b;
	swap(p, q);
	std::cout << *p <<" "<< *q << std::endl;
	return 0;
}

6.2.2节练习

6.11

编写并验证你自己的reset函数,使其作用于引用类型的参数。

#include "Chapter6.h"
#include <iostream>
void reset(int &i) {
	i = 0;
}
int main() {
	int a = 11;
	reset(a);
	std::cout << a << std::endl;
	return 0;
}

6.12

改写6.2.1节中练习6.10(第188页)的程序,使用引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么?

#include <iostream>
#include "Chapter6.h"
void swap2(int &p,int &q) {//引用交换形参
	int temp;//临时变量
	temp = p;//temp获得p指向的地址中的值
	p = q;//p指向的地址中的值换成q指向的地址中的值
	q= temp;//q指向的地址中的值换成temp
}
int main() {
	int a = 10, b = 5;
	swap2(a,b);
	std::cout << a << " " << b<< std::endl;
	return 0;
}

6.13

假设T是某种类型的名字,说明以下两个函数声明的区别:一个是void f (T),另一个是void f (&T)。

A:后者能改变实参T的值。

6.14

举一个形参应该是引用的例子,在举一个形参不能是引用类型的例子。

A:当函数中形参参与的操作,需要对实参有影响时,形参应该是引用类型。当函数中形参参与的操作不会对实参有影响时,不能是引用类型【别人的答案】。

6.15

说明find_char函数中的三个形参为什么是现在的类型,特别说明为什么s是常量引用而occurs是普通引用?为什么s和occurs是引用类型而c不是?如果令s是普通引用会发生什么情况?如果令occurs是常量引用会发生什么情况?

A:因为s太长不能拷贝,所以要引用,因为s的值不需要改变,所以用常量引用。因为要获得多个数的返回,所以occurs是引用。s是普通引用,可能会被改变。occurs是常量引用,则得不到++发生了多少次。

6.2.3节练习

6.16

下面这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。

bool is_empty(string& s)  {  return s.empty(); }

A:只能接受非常量类型,且不能直接接受字面值常量,修改:bool is_empty(const string& s)  {  return s.empty(); }

6.17

编写一个函数,判断string对象中是否含有大写字母。编写一个函数,把string对象全部都改成小写形式。在这两个函数中你使用的形参类型相同吗?为什么?

A:不相同,前者形参使用常量,后者使用非常量

#include <string>
#include <iostream>
bool hasUpper(const std::string& s) {	//判断string对象中是否含有大写字母。
	for (auto i : s) {					//遍历string对象
		if (isupper(i)) {				//如果有大写,返回true
			return true;
		}
	}
	return false;
}
void stringTolower(std::string& s) {	//把string对象全都改写成小写形式
	for (auto& i : s) {					//遍历string对象,震惊这里要加&
		if (isupper(i)) {				//如果有大写,把它变成小写
			i= tolower(i);
		}
	}
}
int main() {
	std::string s = "Hello World!";
	std::cout << hasUpper(s) << std::endl;
	stringTolower(s);
	std::cout<<s<< std::endl;
	return 0;
}

6.18

为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。

(a) 名为compare的函数,返回值布尔值,两个参数都是matrix类的引用。
(b) 名为change_val的函数,返回vector<int>的迭代器,有两个参数:一个是int,另一个是vector<int>的迭代器。 

A:(a)bool compare(const matrix &a,const matrix &b);
(b)vector<int>::iterator change_val(int a,vector<int>::iterator b);

6.19

假定有如下声明,判断哪个调用合法,哪个调用不合法。对于不合法的函数调用,说明原因。

double calc(double);
int count(const string &, char);
int sum(vector<int>::iterator, vector<int>::iterator, int);
vector<int> vec(10);
(a) calc (23.4, 55.1);             (b)count("abcda",'a');
(c)calc(66);                       (d) sum(vec.begin(), vec.end(), 3.8);

A:(a)不合法,参数数量不一致;(b)合法。(c)合法。(d)合法。

6.20

引用形参什么时候应该是常量引用?如果形参应该是常量引用,我们将其设为了普通引用,会发生什么情况?

A:当函数不会改变参数值的时候,应该将形参设为常量引用。若其该为常量引用,而我们将其设为普通引用,当函数内部改变其值,将不会报错。

6.2.4节练习

6.21

编写一个函数,令其接受两个参数,一个是int型,另一个是int指针。函数比较int型值和指针所指值,返回较大的那个。在该函数中,指针的类型应该是什么?

#include <iostream>
#include <vector>
#include <string>
int larger_one(const int i, const int *const p)
{
    return (i > *p) ? i : *p;
}
int main()
{
    int i = 6;
    std::cout << larger_one(7, &i)<<std::endl;

    return 0;
}
————————————————
版权声明:本文为CSDN博主「NotFound1911」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_24739717/article/details/104244699

6.22

编写一个函数,令其交换两个int指针。

#include <iostream>
#include <vector>
#include <string>
using namespace std;
void swap(int*& lft, int*& rht)
{
    auto tmp = lft;
    lft = rht;
    rht = tmp;
}

int main()
{
    int i = 10, j = 20;
    auto lft = &i;
    auto rht = &j;
    swap(lft, rht);
    std::cout << *lft << " " << *rht << std::endl;

    return 0;
}
————————————————
版权声明:本文为CSDN博主「NotFound1911」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_24739717/article/details/104244699

6.23

参考本节介绍的几个print函数,根据理解编写你自己的版本。依次调用每个函数使其输入下面定义的i和j:
int i = 0, j[2] = {0, 1};

我把数组理解成指针可以++,结果它就顺着地址无限制的加下去了。

#include <iostream>
void print(const int i) {
	std::cout << i << std::endl;
}
void print(const int (&arr)[2]) {
	for (auto elem : arr) {
		std::cout << elem << std::endl;
	}
}
int main() {
	int i = 0;
	int j[2] = {0,1};
	print(i);
	print(j);
	return 0;
}

6.24

描述下面这个函数的行为。如果代码中存在问题,请指出并改正。

void print(const int ia[10]) {

for (size_t i =0; i != 10; ++i)

cout << ia[i] << endl; 

}

A:函数的形参是一个常量数组指针,const int * []类型,因此其值不可以修改,然后函数体内循环体中遍历输出了每个数组的元素。并且这个函数的形参的数组大小限定到10个,但是实际上传入的数组大小无所谓,若大于10,则输出前10个没问题,但是如果小于10,不足10个的位置会输出未定义的值【别人的答案】。

6.2.5节练习

6.25

编写一个main函数,令其接受两个实参。把实参的内容连接成一个string对象并输出出来。

没测试过,不知道正不正确。

#include <iostream>
#include <string>
int main(int argc, char *argv[]) {
	std::string s;
	for (int i = 0; i < argc; i++) {
		s += argv[i];
	}
	std::cout << s << std::endl;
	return 0;
}

6.26

编写一个程序,使其接受本节所示的选项;输出传递给main函数的实参的内容。

A:就跟上面一样好了。

6.2.6节练习

6.27

编写一个函数,它的参数是initializer_list<int>类型的对象,函数的功能是计算列表中所有元素的和。

#include <initializer_list>
int sum(std::initializer_list<int> a) {
	int sum = 0;
	for (auto beg = a.begin(); beg != a.end(); ++beg) {
		sum += (*beg);
	}
}

6.28

在error_msg函数的第二个版本中包含ErrCode类型的参数,其中循环内的elem是什么类型?

A:elem的类型是通过编译器推断出来的,其类型是const string &类型。使用引用是为了避免拷贝过长的string类型字符串【别人的答案】。

6.29

在范围for循环中使用initializer_list对象时,应该将循环控制变量声明成引用类型吗?为什么?

A:引用的优势是在于可以直接使用引用,从而达到操作引用绑定的对象,以及为了避免拷贝时过于复杂。由于initializer_list对象中列表的元素都是const对象,不能修改,所以没必要使用引用类型的控制变量,但是若是string类型或者其他容器类型的对象,执行拷贝操作,有时候会拷贝过长的string对象,所以使用引用是为了避免拷贝【别人的答案】。

6.3.2节练习

6.30

编译第200页的str_subrange函数,看看你的编译器是如何处理函数中的错误的。

6.31

什么情况下返回的引用无效?什么情况下返回常量的引用无效?

A:函数返回的引用必须满足:引用的对象是一个在调用函数之前就存在的对象;如果引用的对象只是函数中的一个局部对象,那么函数返回这个引用是无效的。同样,返回的常量的引用有效的条件是:引用的对象是一个在调用函数之前就存在的对象。如果引用的对象只是函数中的一个局部对象,那么函数返回这个引用是无效的【别人的答案】。

6.32

下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。

A:合法。

6.33

编写一个递归函数,输出vector对象的内容。

#include<iostream>
#include<vector>
using namespace std;
void printstr(vector<string> str,vector<string>::size_type i)
{
	if (i < str.size())
	{
		cout << str[i] << endl;
		printstr(str, ++i);
	}
}
int main()
{
	vector<string> str = {"hello","world","I","am","fine"};
	printstr(str, 0);
	return 0;
}

6.34

如果factorial函数的停止条件如下所示,将发生什么情况?

A:如果factorial函数的停止条件改为if(val!=0),那么当输入一个负数时,程序进入死循环【别人的答案】。

6.35

在调用factorial函数时,为什么我们传入的值是val-1而非val--?

A:val -- 返回的是val的值,相当于又把val当作参数传递,递归将永远不会停止,并且第一次递归不断重复执行【别人的答案】。

6.36

编写一个函数的声明,使其返回数组的引用并且该数组包含10个string对象。不要使用尾置返回类型、decltype 或者类型别名。

A:string (&func(int i))[10];

6.37

为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用decltype关键字。你觉的哪种形式最好,为什么?

类型别名:别人的答案

using str = string[10];

str &func(int s);

尾置返回类型:auto func(int i)->string(&)[10];

decltype关键字:

int odd[]={1,2,3,4,5}
decltype(odd) &func(int i);

6.38

修改arrPtr函数,使其返回数组的引用。

int odd[] = {1,3,5,7,9};
Int even[] = {0,2,4,6,8}

decltype(odd) (&arrPtr)(int i)
{
Return (i %2) ? odd : even;
}

6.4节练习

6.39

说明在下面的每组声明中第二条声明语句是何含义。如果有非法的声明,请指出来。

(a) int calc(int, int);

int calc(const int, const int);

(b) int get():

double get();

(c) int *reset (int *);

double *reset (double *);

A:a)非法,已经有一个函数了

b)非法,重名了。 

c)可以。

6.5.1节练习

6.40

下面的哪个声明是错误的?为什么?

(a)int ff(int a,int b=0,int c=0);

(b)char *init(int ht=24,int wd,char bckgrnd);

A:b错误,后面两个没有赋值

6.41

char *init(int ht, int wd=80, char bckgrnd=' ');
(a) init();    (b)init(24,10);    (c)init(14, '*'); 

A:a非法,c与初衷不符

6.42

给make_plural函数(参见6.3.2节,第201页)的第二个形参赋予默认实参's',利用新版本的函数输出单词success和failure的单数和复数形式。

#include <iostream>
#include <string>

using namespace std;

string make_plural(size_t ctr, const string &word, const string &ending = "s") //这里给一个默认实参“s” 
{
    return (ctr > 1) ? word + ending : word;
}


int main()
{
    cout << make_plural(1,"success","") << endl; //单数形式 
    cout << make_plural(1,"failure","") << endl; //单数形式 
    cout << endl;
    cout << make_plural(2,"success","es") << endl; //复数形式 
    cout << make_plural(2,"failure") << endl;   //复数形式 
    return 0;
} 
————————————————
版权声明:本文为CSDN博主「键客小E」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fengzhanghao23/article/details/48464617

6.5.2节练习

6.43

你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么?

(a) inline bool eq(const BigInt&,const BigInt&){...}
(b) void putValues(int *arr, int size);

A:b放在头文件里,a放在源文件里,因为a有函数体【我的答案,好像错了】。

把a的函数和定义放在头文件中,因为a是内联函数。把b的声明放在头文件中,b的定义放在源文件中【别人的答案】。

6.44

将6.2.2节的isShorter函数改写成内联函数。

inline bool isShorter(const std::string& s1, const std::string& s2)
{
	return s1.size() < s2.size();
}

6.45

回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗?如果是,将它们改写成内联函数;如果不是,说明原因。

A:它们不是内联函数。一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。而在前面写的代码虽然规模很小,但并没有去频繁的调用它们【别人的答案】。

6.46

能把isShorter函数定义成constexpr函数吗?如果能,将它改写成constexpr函数;如果不能,说明原因。

A:不能。定义constexpr函数要遵循几项规定:函数的返回类型及所有形参的类型都得是字面值类型,而且杉树他中必须有且只有一条return语句。因为s1.size()和s2.size()返回的结果不是常量表达式,return s1.size()<s2.size()的结果也不是一个常量表达式【别人的答案】。

6.5.3节练习

6.47

改写6.3.2节(第205页)练习中使用递归输出vector内容的程序,使其有条件地输出与执行过程有关的信息。例如,每次调用时输出vector对象的大小。分别在打开和关闭调试器的情况下编译并执行这个程序。

#include<iostream>
#include<vector>
void printstr(std::vector<std::string> str,std::vector<std::string>::size_type i)
{
	if (i < str.size())
	{
		std::cout<<str[i].size()<< std::endl;
		printstr(str, ++i);
	}
}
int main()
{
	std::vector<std::string> str = { "hello","world","I","am","fine" };
	printstr(str, 0);
	return 0;
}

6.48

说明下面这个循环的含义,它对assert的使用合理吗?

string s;

while (cin >> s && s != sought) {} //空函数体

assert(cin);

 A:不合理,当不再输入,或者s = sought时,此时cin为空,表达式求值为假,断言实现,输出错误信息,并退出程序【别人的答案】。

6.6节练习

6.49

什么是候选函数?什么是可行函数?

A:

1.候选函数是指在调用点上其声明是可见的且与被调用的函数同名的函数。

2.可行函数是指从候选函数中选出的函数,必须满足下列条件:函数的形参数目与该函数调用的实参数目相同;每个实参的类型必须与对应形参的类型匹配,或者可以隐式转换为对应形参的类型。

6.50

已知有第217页对函数f的声明,对于下面的每一个调用列出可行函数。其中哪个函数是最佳匹配?如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性?

void f();
void f(int);
void f(int,int);
void f(double,double=3.14);
(a) f ( 2.56, 42)      (b) f(42)       (c) f(42,0)      (d) f(2.56,3.14)

(a)最佳匹配 void f(int,int);【我的答案错误,可能是因为调用void f(double,double=3.14);时,42可以覆盖3.14。正确答案可能时:该调用具有二义性而不合法】

(b)最佳匹配void f(int);

(c)最佳匹配void f(int,int);

(d)最佳匹配void f(double,double=3.14);

6.51
编写函数f的4个版本,令其各输出一条可以区分的消息。验证上一个练习的答案,如果你回答错了,反复研究本节的内容直到你弄清自己错在何处。

#include<iostream>

using namespace std;

void f()
{
    cout << "f() " << endl;
}

void f(int x)
{
    cout << "f(int) " << x << endl;
}

void f(int x, int y)
{
    cout << "f(int,int) " << x << " " << y << endl;
}

void f(double x, double y=3.14)
{
    cout << "f(double,double) " << x <<" " << y << endl;
}

int main()
{
    //f(2.56, 42);
    f(42);
    f(42, 0);
    f(2.56, 3.14);
    return 0;
}
f(int) 42
f(int,int) 42 0
f(double,double) 2.56 3.14

https://zhuanlan.zhihu.com/p/182306699

6.61节练习

6.52

已知有如下声明,
void manip(int,int);
double dobj;
请指出下列调用中每个类型转换的等级(参见6.61节,第219页)。
(a)manip('a','z');
(b)manip(55.4,dobj);

A:(a) 类型提升实现的匹配 (b) 算术类型转换实现的匹配【别人的答案】

6.53

说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)。

(a) int calc(int&, int&); 
    int calc(const int&, const int&); 
(b) int calc(char*, char*);
    int calc(const char*, const char*);
(c) int calc(char*, char*);
    int calc(char* const, char* const); 

 A:

(a)常量引用会调用第二条语句

(b)当调用的指针指向常量字符时,调用第二条语句

(c)当指向字符的指针是常量时,调用第二条语句【我的答案错误,不合法,顶层const不影响传入函数的对象。】

6.7节练习

6.54

编写函数的声明,令其接受两个int形参并且返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。
A:int fun(int,int);
vector<decltype(fun) *> vec;

6.55

编写4个函数,分别对两个int值执行加、减、乘、除运算;在上一题创建的vector对象中保存指向这些函数的指针。

#include<iostream>
#include<vector>
int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
std::vector<decltype(add) *> vec{ *add, *sub,*mul,*div };

int add(int x, int y) {
	return (x + y);
}
int sub(int x, int y) {
	return (x - y);
}
int mul(int x, int y) {
	return (x*y);
}
int div(int x, int y) {
	//我不知道没有返回类型怎么办
	if (y == 0) {
		std::cout << "除数不能为0" << std::endl;
	}
	else {
		return (x / y);
	}
}

6.56

调用上述vector对象中的每个元素,并输出结果。

int main() {
	for (auto x : vec)
	{
		std::cout << (*x)(4, 7) << std::endl;
	}
	return 0;
}

 

这篇关于《C++Primer(第五版)》第六章课后习题答案的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!