C++Primer第六章(习题解答):函数

函数基础

6.1节练习

6.1
实参与形参的区别
答:形参出现在函数定义的地方,实参出现在函数调用的地方,实参的类型和个数与形参对应。
6.2
请指出下列函数的错误?
(a)int f(){
	string s;
	//...
	return s;
	}
答:返回值不匹配,应该string f()
(b)f2(int i){}
答:没有显示返回值的类型,可以为int f(int i){}
(c)int calc(int v1,int v1){}
答:参数不能相同
(d)double square(double x)return x*x;
答:函数体必须要放在一对花括号里
6.3
编写你自己的fact函数,上机检查是否正确。
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;
int fact(int n) {
	if (n == 1)return 1;
	return n * fact(n - 1);
}
int main() {
	cout << fact(5) << endl;
	return 0;
};
6.4
编写一个用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘,在main中调用这个函数。
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;
int fact(int n) {
	if (n == 1)return 1;
	return n * fact(n - 1);
}
int order() {
	int n;
	cin >> n;
	return fact(n);
}
int main() {
	cout << order() << endl;
	return 0;
};
6.5
编写一个函数实现实参的绝对值
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;
int my_abs(int n) {
	if (n > 0)
		return n;
	else
		return -n;
}
int main() {
	int n;
	cin >> n;
	cout << my_abs(n) << endl;
	return 0;
};

局部对象

6.1.1节练习

6.6
说明形参,局部变量,以及局部静态变量的区别。
答:形参是函数定义时所用,局部变量是自动对象,是在函数结束后消失的,而静态局部变量其生命周期会直到程序销毁。
6.7
编写一个函数,当它第一次被调用时返回0,以后每次被调用加一。
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;
int ret() {
	static int ret = 0;
	return ret++;
}
int main() {
	cout << ret() << endl;
	cout << ret() << endl;
	cout << ret() << endl;
	return 0;
};

函数声明

6.1.2节练习

6.8
编写一个名为Chapter6.h的头文件,令其包括6.1节练习中的函数声明。
答:
int fact(int n);

分离式编译

6.1.3节练习

6.9
编写你自己的fact.cc和factMain.cc,这两个文件都应该包含上一节的练习中编写的Chapter6.h头文件,通过这些文件,理解你的编译器如何支持分离式编译。
答:
factMain.cc
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include"Chapter6.h";
using namespace std;
int main() {
	cout << fact(5) << endl;
	return 0;
};
fact.cc
#include"Chapter6.h"
int fact(int n) {
	if (n == 1)return 1;
	return n * fact(n - 1);
}

参数传递

6.2.1节练习

6.10
编写一个函数,使用指针形参交换两个整数的值,在代码中使用该函数并输出交换后的结果,以此验证函数的正确性。
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include"Chapter6.h";
using namespace std;
void my_swap(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}
int main() {
	int a = 3, b = 5;
	cout << a << " " << b << endl;
	my_swap(&a, &b);
	cout << a << " " << b << endl;
	return 0;
};

6.2.2节练习

6.11
编写并验证你自己的reset函数,使其作用于引用类型的参数。
答:
void reset(int &i){
	i=0;
}
6.12
改写6.2.1节练习的程序,使用引用而非指针交换两个整数的值,你觉得那种方法更好。
答:
void my_swap(int &a,int &b){
	int temp=a;
	a=b;
	b=temp;
}
引用更好。
6.13
假设T是某种类型的名字,说明以下两个函数声明的区别,一个是void f(T),另一个是void f(&T)
答:形参不同,一个是值传递,一个是引用传递。
6.14
举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。
答:引用传递相比于值传递有三个优点,
1.可以直接操作引用的对象
2.可以加快效率
3.可以返回多个值
6.15
说明为什么find_char函数的三个参数是现在的类型,特别说明为什么
s是常量引用,occurs是普通引用?为什么s和occurs是引用类型而c不是
如果令s是普通引用会发生什么,如果令occurs是常量引用会发生什么情况。
答:s是常量引用是由于不需要改变s的值以及引用的效率更高一些,而occurs是需要改变occurs的值的。

6.2.3节练习

6.16
下面的这个函数虽然合法,但是不是特别有用。指出它的局限性并设法改善。
bool is_empty(string&s){return s.empty();};
答:
应该弄成常量引用,这样那些字符串字面值也可作为参数。
6.17
编写一个函数,判断string对象中是否含有大写字母编写一个函数,把string对象全部改写为小写形式,在这两个函数中使用的形参类型相同吗?
答:不同,一个是常量应用,一个是普通引用
6.18
为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。
(a)名为compare的函数,返回布尔值,两个参数都是matrix类的引用
答:bool compare(matrix &a,matrix&b)比较两个矩阵的大小
(b)名为change_val的函数,返回vector<int>的迭代器,有两个参数,一个是int 一个是vector<int>的迭代器。
答:vector<int>::iterator change_val(int a,vector<int>::iterator)
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);//正确
6.20
引用形参什么时候应该是常量引用?如果形参应该是常量引用,为我们将其设为了普通引用,会发生什么情况?
答:不涉及改变引用的对象时尽量使用常量引用,如果本来是常量引用设为普通引用,可能对于那些字符产字面值作为参数会出错。

6.2.4节练习

6.21
编写一个函数,令其接受两个参数:一个是Int型指针,一个是int型数。函数比较int值和int指针指向的值,返回较大的那个,在这函数中指针的类型应该为什么。
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include"Chapter6.h";
using namespace std;
int big(const int a, const int* b) {
	return a > *b ? a : *b;
}
int main() {
	int a = 9, b = 10;
	cout << big(a, &b);
	return 0;
};
6.22
编写一个函数,令其交换两个Int指针。
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include"Chapter6.h";
using namespace std;
void my_swap(int* &a, int*&b) {
	int* temp = a;
	a = b;
	b = temp;
}
int main() {
	int a = 7, b = 10;
	int *p = &a, *q = &b;
	my_swap(p, q);
	return 0;
};
6.23
参考本节的几个print函数,根据理解编写自己的版本,以此调用函数使其输入下面定义的i和j
int i=0,j[2]={0,1};
答:
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include"Chapter6.h";
using namespace std;
void print(const int* p,int size,const int size_num) {
	if (p) {
		while (size)
			cout << *p++, size -= size_num;
	}
	cout << endl;
	return;
}
int main() {
	int i = 0;
	int j[2] = { 0,1 };
	print(&i, sizeof(i), sizeof(int));
	print(j, sizeof(j), sizeof(int));
	return 0;
};
6.24
描述下面这个函数的问题,如果代码中存在问题,请指出并指正。
void print(const int ia[10]){
for(size_t i=0;i!=20;++i){
	cout<<ia[i]<<endl;
}
}
答:指针越界了

6.2.5节练习

6.25
编写一个main函数,令其接受两个实参,把实参的内容连接成一个string对象并输出出来。
答:
#include<iostream>
using namespace std;
int main(int argc,int **argv) {
	string s;
	for (int i = 1; i != 3; ++i)
		s += argv[i];
	return 0;
};
6.26
编写一个程序,使其接受本文所示的选项,输出传递给main函数实参的内容。
答:
#include<iostream>
using namespace std;
int main(int argc,int **argv) {
	string s;
	for (int i = 1; i != argc; ++i)
		s += argv[i];
	return 0;
};

6.2.6节练习

6.27
编写一个函数,它的参数是initializer_list<int>类型的对象,函数的功能是计算列表中的所有元素的值。
答:
#include<iostream>
using namespace std;
int my_sum(initializer_list<int>num) {
	int sum = 0;
	for (auto& nums : num) {
		sum += nums;
	}
	return sum;
}
int main() {
	cout << my_sum({ 1,2,3,4 }) << endl;
	return 0;
};
6.28
在error_msg函数中的第二个版本中中包含ErrorCode类型的参数,其中循环的elem是什么类型。
答:
const string&类型
6.29
在范围for循环中使用initializer_list对象时,应该将循环控制变量声明为引用类型吗?
答:都差不多,反正initializer_lsit中元素是常量,不可改变。

6.3.2节练习

6.30
编译200页的str_subrange函数,看看你的编译器是如何处理函数中的错误的。
答:显示必须要给返回值
6.31
什么情况下返回的引用无效,什么情况下返回常量的引用无效?
答:返回的是局部引用时无效。
6.32
下面的函数合法吗?
int &get(int*arry,int index){return arry[index];}
int main(){
int ia[10];
for(int i=0;i!=10;++i)
	get(ia,i)=i;
}
答:合法
6.33
编写一个递归函数,输出vector对象的内容
答:
#include<iostream>
#include<vector>
using namespace std;
void  print(vector<int>& ans,int size) {
	if (size < 0)return;
	print(ans, size - 1);
	cout << ans[size] << " ";
}
int main() {
	vector<int>ret = { 1,2,3,4 };
	print(ret, ret.size()-1);
	return 0;
};
6.34
如果factorial函数停止条件为if(val!=0)将会发生什么情况。
答:
其结果永远为0
6.35
在调用factorial函数时,为什么我们传入的值为val-1而不是val--
答:
变量的读取操作与递减操作存在于同一条表达式中,可能会产生未定义的值。

6.3.3节练习

6.36
编写一个函数声明,使其返回数组的引用并且该数组包含10个string对象,不要使用尾置返回类型,decltype或者类型别名。
答:
string (&func())[10];
6.37
为上一题的函数再编写三个声明,一个使用类型别名,一个使用decltype,一个使用尾置返回类型。
答:
类型别名:
typedef string arry[10];
arry &func();
decltype:
string array[10];
decltype(array) &func()
尾置返回类型:
auto func()->string(&) [10];
6.38
修改arrPtr函数,使其返回数组的引用。
答:
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);
答:错误,顶层const不算重载
(b)int get()
double get()
答:返回值不同不算重载
(c)int *reset(int*);
	double *reset(double*);
答:正确

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);//错误
一旦某个形参被赋予默认值,则其后的形参也需要赋予默认值
6.41
下面哪个调用是非法的?哪个调用虽然合法但与程序员的初衷不符。
char *init(int ht,int wd=80,char bckgrd=' ');
(a)init()//错误,第一个参数必须要赋值
(b)init(24,10)//正确
(c)init(14,'*')//第二个参数应该为整数,这个是char型,不符合初衷
6.42
给make_plural函数第二个形参赋予默认实参's',利用新版本函数输出单词success何failure的单数何复数形式。
答:
#include<iostream>
#include<vector>
using namespace std;
string make_plural(size_t ctr, const string& word="s", const string& ending="") {
	return (ctr > 1) ? word + ending : word;
}
int main() {
	cout << make_plural(1, "sucess") << endl << make_plural(1, "failure") << endl<<make_plural(2, "sucess", "es") << endl << make_plural(2, "failure", "s");
	return 0;
};

6.5.2节练习

6.43
你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么?
(a) inline bool eq(const BigInt&,const BigInt&){...}
(b)void putValues(int *arr,int size);
答:a放在头文件中,b放在头文件中,因为内联函数可以定义多次,放在头文件中可以使定义统一。
6.446.2.2节的isShorter函数改写成内联函数。
答:再函数头部加入inline关键字即可
6.45
回顾在前面的练习中写的函数,是内联函数吗?
答:不是,需要调用,而且没有inline关键字修饰
6.46
能把isShorter函数定义为consttexpr函数吗?
答:
不能,return语句无法构成常量表达式

6.5.3节练习

6.47
改写6.3.2节(205页)练习中使用递归输出vector内容的程序,使其有条件地输出与执行过程有关的信息。例如:每次调用时输出vector对象的大小。分别在打开和关闭模式的情况下编译并执行这个程序。
答:
#include<iostream>
#include<vector>
#define NDEBUG
using namespace std;
void  print(vector<int>& ans, int size) {
#ifndef NDEBUG
	cout << __func__ << "size of vector is:" << ans.size() << endl;
#endif
	if (size < 0)return;
	print(ans, size - 1);
	cout << ans[size] << " ";
}
int main() {
	vector<int>ret = { 1,2,3,4 };
	print(ret, ret.size() - 1);
	return 0;
};
6.48
说明下面这个循环的含义,它对assert的使用合理吗?
string s;
while(cin>>s&&s!=sought){};
assert(cin);
答:
不合理,当用户尝试终止输入时,会assert(),不合初衷

6.6节练习

6.49
什么是候选函数?什么是可行函数?
答:
候选函数:
1.与被调函数同名
2.其函数声明可在调试点可见
可行函数:
1.其形参数量与调用的形参数量相同
2.其形参类型对应相同或者能够转化。
6.50
已知有第217页对函数f的声明,对于下面每一个调用列出可行函数。其中哪个函数匹配最佳?
(a)f(2.56,42)//没有,二义性
(b)f(42)//f(int)
(c)f(42,0)//f(int,int)
(d)f(2.56,3.14)//f(double,double=3.14)
6.51
编写函数f的四个版本,令其输出一条可以区分的消息,验证上一题的答案。
答:
#include<iostream>
#include<vector>
#define NDEBUG
using namespace std;
void f() {
	cout << "f()" << endl;
}
void f(int n) {
	cout << "f(int)" << endl;
}
void f(int a, int b) {
	cout << "f(int a,int b)" << endl;
}
void f(double a, double b= 3.14) {
	cout << "f(double a,double b=3.14) " << endl;
}
int main() {
	//f(2.56, 42);
	f(42);
	f(42, 0);
	f(2.56, 3.14);
	return 0;
};

6.6.1节练习

6.52
已有如下声明:
void manip(int ,int)
double dobj;
请指出下列调用中每个类型转换的等级
答:
(a)manip('a','z')
3类型转换
(b)manip(55.4,dobj)
4算术类型转换
6.53
说明下列每组声明中第二条语句会产生什么影响。
(a)int calc(int&,int&)
int calc(const int&,const int&)
底层const,可区分函数
(b)int calc(char*,char*)
int calc(const char*,const char*);
底层const 可以区分函数
(c)int calc(char*,char*)
int calc(char*const ,char*const)
顶层const,无法区分函数

6.7节练习

6.54
编写函数的声明,令其接受两个int形参并且返回类型也是int,然后声明一个vector对象,令其元素是指向该函数的指针。
6.55
编写4个函数,分别对两个int值执行加减乘除运算,在上一题创建的vector对象中保存指向这些函数的指针。
6.56
调用上述vector对象中的每个元素并且输出其结果。
答:三题的代码如下:
#include<iostream>
#include<vector>
#define NDEBUG
using namespace std;
int fun1(int a, int b) {
	return a + b;
}
int fun2(int a, int b) {
	return a - b;
}
int fun3(int a, int b) {
	return a * b;
}
int fun4(int a, int b) {
	return a / b;
}
void Compute(int a, int b, int(*p)(int, int)) {
	cout << p(a, b) << endl;
}
int main() {
	typedef int (*func)(int a, int b);
	vector<func>ans = {fun1,fun2,fun3,fun4};
	for (auto s : ans) {
		Compute(2, 3, s);
	}
	return 0;
};
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值