c++ primer 第五版----第六章习题解答

来源:互联网 发布:淘宝一千零一夜男主角 编辑:程序博客网 时间:2024/06/06 06:31

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

答:形参在函数定义的形参表中进行定义,是一个变量,其作用域为整个函数。
      而实参出现在函数调用中,是一个表达式。进行函数调用时,用传递给函数的实参对形参进行初始化。
6.2   请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?

(a) int f(){    string s;// ...    return s;}(b) f2(int i){    /* ... */}(c) int calc(int v1, int v1){    /* ... */}(d) double square (double x) return x * x;
答:(a)函数所定义的返回值类型是 int类型,而实际上return返回的表达式的类型是string
     类型,两个类型不同,而string类型又不能隐式转换为int类型。
修改:
string f()
           {
         string  s;
         //......
          return s;
            }
  (b) 因为该函数定义中没有指定的返回值类型,在标准C++中,定义函数时不指定返回值类型
 是非法的。
 修改:
 int f2(int i) {/*......*/ }
  (c) 任意两个形参都不能同名,而且缺少左花括号。
 修改:
 int calc(int v1, int v2) {/*.....*/ }
  (d)缺少一对括住函数体的花括号
 修改:
 double square(double x) { return x * x; }


6.3 -6.4  编写你自己的fact函数,上机检查是否正确。编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数。

int fact(){int c;int s=1;cout << "enter num: " << endl;cin >> c;while (c > 1){s *= c--;}return s;}

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

void ABS(int i){    i >= 0 ? cout << i : cout << -i;}

6.6   说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。

答:形参是函数的一部分,是自动创建的,属于局部变量.局部变量包括形参和函数体内定义的变量.局部静态变量则是定义在函数体内,但是一直到程序结束时才被销毁。

#include<iostream>  using namespace std;  //用于辅助求阶乘的函数  int fac(int x)  {      static int result = 1;    //静态变量      result *= x;      return result;  }    int main()  {     int upLmt;       //upLmt为普通局部变量     cout <<"Enter value of upper limit : "<<endl;     cin >> upLmt;     //依次输出1到X之间的所有阶乘     for(int i = 1;i <= upLmt; ++i)         cout <<i<<"! = "<<fac(i) <<endl;      return 0;  }  

6.7  编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1。

int call(){static size_t t = 0;return t++;}

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

#ifndef CHAPTER6_H#define CHAPTER6_Hint fact(int);#endif复制代码

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

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

void SWAP(int *p1, int *p2){    int temp = *p1;    *p1 = *p2;    *p2 = temp;}

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

#include <iostream>using namespace std;void reset(int &i){    i *= 2;}int main(int argc, char const *argv[]){    int j = 19;    reset(j);    cout << j << endl;    return 0;}

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

#include <iostream>#include "Chapter6.h"using namespace std;void SWAP(int &r1, int &r2){    int temp = r1;    r1 = r2;    r2 = temp;}int main(int argc, char const *argv[]){    int i = 22, j = 5;    cout << "before swap:i=" << i << ",j=" << j << endl;    SWAP(i, j);    cout << "after swap:i=" << i << ",j=" << j << endl;    return 0;}


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

答:第一个是传值形参,第二个是传引用形参,传值形参需要拷贝,引用无需拷贝

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

答:当需要改变实参的值时,应该使用引用.当实参无法被引用形参绑定时,则不能是引用.例如:

#include <iostream>using namespace std;void fun1(int &i){    i *= 2;}void fun2(int &i){    /*...*/}int main(int argc, char const *argv[]){    int j = 5;    fun1(j);        //需要改变j的值    fun2(5);        //错误,此时无法绑定到字面值    return 0;}

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

答:因为不需要也不允许改变s的值,同时为了避免拷贝s,所以s是const引用.occurs的值是可能被改变的,所以不能是const,另外occurs的值需要被保存,所以不可以使用局部变量,而要用引用.c的值不是引用则允许传递一个左值或者右值.如果s是普通引用,那么s可能会被改变.如果occurs是const引用,则程序无法改变字符出现的顺序.

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

bool is_empty(string& s){     return s.empty(); }
答:形参是非const引用,只能绑定string的非常量对象,无法绑定const对象或者字符串字面值.应该将形参修改为const引用。

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

bool string_up(const string s){bool flag = 0;for (auto c : s){if (toupper(c)){flag = 1;break;}}return flag;}string string_down(string &s){for (auto &c : s){c = c + 32;}return s;}

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

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

(a) bool compare(const matrix &m1, const matrix &m2);(b) vector<int>::iterator change_val(const int i, vector<int>::iterator it);

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)不合法,参数数量不匹配
(b)合法
(c)合法
(d)合法

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

答:当不需要改变实参的值或者是个字面值类型时应该是常量引用.如果改为普通引用,则只能绑定非const实参,且实参需要是左值。

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

#include <iostream>using namespace std;int fun(int i, int * const p){    if (p == 0)        throw;    (*p > i) ? *p : i;}int main(int argc, char const *argv[]){    return 0;}

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

void swapP(int* &p1, int* &p2){int *tmp = p1;p1 = p2;p2 = tmp;}

6.23 参考本节介绍的几个print函数,根据理解编写你自己的版本。依次调用每个函数使其输入下面定义的i和j:
int i = 0, j[2] = { 0, 1 };
void print(const int *beg, const int *end)//首尾元素指针{while (beg != end){cout << *beg++ << endl;}}void print(const int ia[], size_t size)//专门定义一个表示数组的大小的形参{for (size_t i = 0; i != size; ++i){cout << ia[i] << endl;}}void print(int (&a)[2])//数组引用形参{for (auto elem : a){cout << elem << endl;}}

6.24  描述下面这个函数的行为。如果代码中存在问题,请指出并改正。
void print(const int ia[10]){    for (size_t i = 0; i != 10; ++i)        cout << ia[i] << endl;}

答:该函数遍历形参传入的数组,缺陷是函数的形参并不能直接传入数组,数组传入形参时是转换成指针进行处理的,转换成指针时,数组维度信息丢失,也即不检查数组的长度。

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

#include <iostream>using namespace std;int main(int argc, char const *argv[]){    string s = argv[1];    s += argv[2];    cout << s << endl;    return 0;}

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

#include <iostream>using namespace std;int main(int argc, char const *argv[]){    for (int i = 1; i != argc; ++i)        cout << argv[i] << '\t';    return 0;}

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

void add_list(initializer_list<int> il){auto s=0;for ( const auto &c : il)//引用绑定{s = s + c;}cout << s << endl;}

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

答:  const string类型。

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

 答:应该,因为initializer_list对象中的元素是常量.

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

答:当返回的是局部变量的引用时无效,当需要修改变量时,返回常量引用无效。

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

复制代码
int &get(int *array, int index) { return array[index]; }int main(){    int ia[10];    for (int i = 0; i != 10; ++i)        get(ia, i) = i;}

合法,因为返回的引用是对指针解引用后的值的引用,解引用后的值是非局部变量.


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

vector<string> vec(const vector<string> &s, vector<string>::iterator iter){if (iter != s.end()){cout << *iter << endl;return vec(s, (iter + 1));}return{};}


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

答:因为负数没有阶乘,所以传入负数时,将产生未定义行为。栈溢出。

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

答:如果是val--,将始终使用val的值去调用递归函数,这样造成死循环。

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

string (&fun()) [10];

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

复制代码
using ref_arr = string (&)[10];ref_arr fun();auto fun() -> string (&)[10];string arr[10];decltype(arr) & fun();

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

decltype(odd) & arrPtr(int i)

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)非法,顶层const无法与没有顶层const的形参区分开来

(b)非法,函数重载不通过返回值来区分
(c)合法

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

 (a) int ff(int a, int b = 0, int c = 0);
 (b)   char *init(int ht = 24, int wd, char backgrnd);

答:b是错误的。默认参数后面的形参都应该给出默认值。


6.41  下面哪个调用时非法的?为什么?哪个调用虽然合法但是显然与程序员的初衷不符?为什么?

char *init(int ht, int wd = 80, char backgrnd = ' ');
(a)  init();
(b)  init(24,10);
(c)  init(14,'*');
答:
(a)不合法,第一个形参要初始化,因为它没有默认参数。
(b)调用合法,也完全符合程序猿的初衷
(c)调用合法,但是不符合程序员的初衷。‘*’是一个char类型,可以转化成 int类型在传递给形参wd.


6.42  make_plural函数(201页)的第二个形参赋予默认实参‘s’,利用新版本的函数输出单词success和fialure的单数和复数形式。

#include<iostream>#include<string>using namespace std;string make_plural(size_t ctr, const string &word, const string &ending = "s"){return (ctr > 1) ? word + ending : word;}int main(){cout << make_plural(2, "success", "es") << endl;cout << make_plural(2, "fialure") << endl;cout << make_plural(1, "success") << endl;cout << make_plural(1, "fiallure") << endl;return 0;}


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

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

(a)将声明和定义都放在头文件中,因为内联函数允许在程序中多次定义,并且仅有函数头声明是不够的,还需要函数的定义
(b)放在头文件和源文件中.因为可以让编译器检查函数的声明和定义是否一致

6.44  将6.2.2节(第189页)的isShorter函数改写成内联函数。
inline bool isShorter(const string &s1, const string &s2){    return s1.size() < s2.size();}


6.46  能把isShorter函数定义成constexpr函数吗?如果能,将它改写成constexper函数,如果不能,说明原因。 
答:不能定义成constexpr函数,因为主要的比较过程,是比较s1.size(),s2.size(),而在函数体中,这两个值是无法确定的,只有编译链接后才能确定,因为,它们不能构成常量表达式,也就无法定义成constexpr函数。 (答案不一定)


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

#include <iostream>#include <vector>using namespace std;void print(vector<string> vec, vector<string>::size_type n) // {    if (n != vec.size()-1)           print(vec,n+1);      cout << vec[n] <<" ";   }void print_debug(vector<string> vec, vector<string>::size_type n) //关闭调试状态 {    #ifndef NDEBUG    cerr << __func__ << ": size is " << vec.size() << endl;    #endif     if (n != vec.size()-1)       cout << vec[n] <<" ";      print(vec,n+1);   }void print_debug_2(vector<string> vec, vector<string>::size_type n)//打开调试状态 {    cerr << __func__ << ": size is " << vec.size() << endl;    if (n != vec.size()-1)       cout << vec[n] <<" ";      print(vec,n+1);   }int main(){    vector<string> vec;    string s;    while (cin >> s)        vec.push_back(s);    vector<string>::size_type n = 0;     print(vec,n);    cout << endl;    print_debug(vec,n);    cout << endl;    print_debug_2(vec,n);    return 0;} 


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

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


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

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

答:候选函数就是在一次函数调用中,确定与被调函数同名且作用域有效的函数集合.可行函数则是在候选函数中选出与被调函数的参数数量和类型一致的函数集合.


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

(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)
没有最佳匹配,存在二义性
(b)
void f(int)
void f(double,double=3.14)
第一个是最佳匹配
(c)
void f(int,int)
void f(double,double=3.14)
第一个是最佳匹配
(d)
void f(int,int)
void f(double,double=3.14)
第二个是最佳匹配

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

#include<iostream>#include<string>using namespace std;void f(){cout << "type f 1!" << endl;}void f(int){cout << "type f 2!" << endl;}void f(int, int){cout << "type f 3!" << endl;}void f(double, double = 3.14){cout << "type f 4!" << endl;}int main(){//f(2.56, 42);f(42);f(42, 0);f(2.56, 3.14);return 0;}


6.52   已知有如下声明:

void manip(int,int); 

double dobj;

请指出下列调用中每个类型转换的等级。

(a)manip('a','z');(b)manip(55.4,dobj);

答:(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)、(b)实现重载。(c)不合法。


6.54  编写函数的声明,令其接受两个int形参并且返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。

答:int f(int,int);

using  p=int(*)(int ,int);

vector<p>vec;


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

#include<iostream>  #include<vector>    using namespace std;    //加法函数  int count_add(int val1, int val2)  {      /*cout << "进行加法运算,输入两个整数: " << endl;     cin >> val1 >> val2;*/      return val1 + val2;  }  //减法函数  int count_sub(int val1, int val2)  {      return val1 - val2;  }  //乘法函数  int count_mul(int val1, int val2)  {      return val1*val2;  }  //除法函数  int count_div(int val1, int val2)  {      return val1/val2;  }        int main()  {      using pf = int(*)(int, int);      //pf 是指针类型      vector<pf>  vect{ count_add, count_sub, count_mul, count_div };      for (auto &c : vect)      {          cout << c(4,2) << endl;      }      return 0;  }  


参考链接:http://www.cnblogs.com/pluse/p/5133564.html

                  http://blog.csdn.net/u010830200/article/details/34416129


原创粉丝点击