c++ primer plus 第六版 第七章重点内容总结 以及 编程题答案

11 篇文章 0 订阅

1.函数基础

对于有返回值的函数必须返回结果必须为typename类型或者可以被转换为typename类型。C++的返回类型不能是数组。但可以是其他任何类型——整数、浮点数、指针、结构和对象。不过c++虽然不能返回数组,但是可以将数组作为结构或者对象组成部分来返回。函数通过将返回值复制到指定的cpu寄存器或者内存单元中将其返回。

 

Main函数中使用函数名和参数(后面跟一个分号)来调用void类型的函数。有返回值的函数可以用于赋值语句中。

 

函数原型不要求提供变量名(可省略),原型中的变量名不必与定义中的变量名相同。提供原型函数使得编译器正确处理函数返回值,还可以检查使用的参数数目,参数类型是否正确,即使不正确在可能的情况下转换为可能的情况。仅当有意义的情况下原型化才会导致类型转换,如原型不会将整数转换为结构或者指针。在编译阶段进行的原型化被称为静态类型检查。其可以捕获很多在运行阶段难以捕获的错误。

 

函数参数部分:c++通常按照值进行传递,传递给形参使用的值是实参的副本,而不是原来的数据。因此参数传递将参数赋给参量。在函数中声明的变量(参数)是该函数私有的,在函数被调用时计算机将为这些变量分配内存,函数结束时将释放这些内存。也叫自动变量因为在程序中自动分配和释放内存。形参和其他局部变量的的主要区别是:形参从所调用的函数那里获得自己的值,而其他变量是从函数中获得自己的值。

 

2.函数与数组

大多数情况下,c++将数组名视为指针。前几章提过c++将数组名解释为其第一个元素的地址,cookies==&cookies[0].该规则有一些例外,1数组声明使用数组名来标记存储位置,2对数组名使用sizeof()将得到整个数组的长度(以字节为单位),3将地址运算符用于数组名时将返回整个数组的地址,如&cookies将返回一个32字节内存块的地址。

 

比较函数原型: int sum_arr(int arr[],int n);   int sum_arr(int *arr,int n);

调用时int sum=sum_arr(cookies,size);cookies是数组名,是其第一个元素的地址,因此传递的是地址,因为数组元素是int类型,即cookies类型是int型指针,int *。这两个函数原型都是正确的,当且仅当用于函数头或者函数原型中,int *arr和int arr[]的含义才是相同的。当指针指向数组的第一个元素时本书采用数组表示法,当指针指向一个独立的值时采用指针表示法。在其他的上下文中两者的含义并不相同。目前而言记住以下两个恒等式:

arr[i]==*(ar+i);&arr[i]==ar+i;注意:将指针(包括数组名)+1实际上是加了一个指针指向类型的长度(字节为单位),在遍历数组时,使用指针加法和数组下标是等效的。

 

传递常规变量时,函数使用变量的拷贝,但传递数组的地址而不是内容,因此使用原数组。将一个数组传递给被调用函数时,形参是一个指向该数组的新指针,当sizeof作用于其上时,往往代表该指针的长度。记住必须显示传递数组长度是因为指针本身并没有指出数组长度。可以对函数提供任意的元素数量和起始元素地址进行计算 sum=sum_arr(cookies,3);sum=sum_arr(cookies+4,4);如可以采用如上方式传递数组,但一定要单独传递数组长度。

 

因为接受数组名的函数将使用原始函数,为防止函数无意中修改数组的内容可在声明形参时使用关键字const。void show_array(const double arr[],int n);其中指针arr是常量数据,意味着不能使用arr来修改数据。并注意,这不是说原始数组必须是常量,而只是意味着不能在show_array函数中修改数据。因此该函数将数组视为只读数组。

 

处理数组的c++函数通常做法是,将指向数组起始处的指针(指出数组的位置和类型)作为一个参数,将数组长度作为第二个参数,另一种方法是指定元素区间,通过传递一个指向数组开头的指针和指向数组结束的指针。STL方法使用“超尾”概念来指定区间。也就是说对于数组而言,标识数组结尾的参数将是指向最后一个元素后面的指针。例如double elbuod[20];则指针elbuod和elbuod+20定义了区间。数组名指向第一个元素,elbuod+19指向第二个元素,所以elbuod+20指向数组结尾后面的位置。注意修改的意思是操作过后,数组的元素本身发生改变。只是使用了它的数据不算修改。

 

如int sum_arr(const int *begin,const int *end);调用时可以直接使用int sum=sum_ar(cookies+8,cookies+4);指针cookies+size指向最后一个元素后面的位置,因为数组有size个元素,因此cookies[size-1]是最后一个元素,其地址是cookies+size-1。

 

3.const的微妙所用

 

可以用两种不同的方式将const关键字应用于指针。第一种是让指针指向一个常量对象,可以防止使用该指针来修改所指向的值。第二种是将指针本身声明为常量,这样可以防止改变指针指向的位置。

 

如 int age=39;const int * pt=&age;这意味着pt指向一个const int类型(不意味着指向的值值一定是常量,而是pt所言这个值是常量),即不可通过pt来修改age。但age不是常量,因此可以通过age变量修改age 的值。即常规变量的地址可以赋给常规指针/const类型指针。也可以将const变量的地址赋给指向const的指针,但是不能将const的地址赋值给常规指针(这意味着可以通过常规指针修改本不该修改的const值),如果非要这么做那么必须采取强制类型转换来做。

 

如果指针指向指针,那么const更加复杂。涉及一级间接关系时,可以将非cosnt指针赋给const指针。如

int age=39;int *pd=&age; const int *pt=pd;

但是涉及二级指针时,将变得不再使用如下:
const int **pp2; int *p1; const int n=13; pp2=&p1//invalid,but suppose pp2 point to p1 *pp2=&n//valid,both const,sets p1 to point to n, *p1=10;//valid,but changes const n

 

如果可以则应将指针形参声明为指向const的指针,这样可以避免由于无意间修改数据而导致的编程错误。使用const使得函数能够处理const和非const实参,否则只能接受非const数据。

 

4.指向const 的指针和const指针

另const的一个微妙之处如 int age=39; const int *pt =&age; const只能防止修改pt指向的值,而不能防止修改pt的值,即可将新地址赋值给pt, int sage=80;pt=&sage;但仍然不能修改其指向的80这个值。若想使得指针不变那么可以采用以下方式声明:

int * const finger =&sloth;这使得finger只能指向sloth,但允许使用finger来修改sloth 的值。如果更进一步当然可以声明指向const对象的const指针。double trouble=2.0E30; const double *const stick=&trouble;

 

5.函数和二维数组

 

以简单的例子标记开始,声明语句使用int data[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};函数调用语句是 int total =sum(data,3);而函数原型是 int sum(int (*ar2)[4],int size); 或者int sum(int ar2[][4],int size)两种。可以分析原二维数组的构成是三个元素集合(三行),每个元素中涵盖四个int值。注意声明语句声明的是一个指向由4个int值组成的数组的指针,真正的定义方式应该是定义指向n维数组的指针。即它指向四个int值构成的数组也就是已经指定了列数从而不需将该参数单独传递。()不能少,因为假如 int *ar2[4]声明一个由四个指向int类型的指针构成的数组。以下是一个函数定义可用的模板:

int sum(int (*ar2)[4],int size)

{

   int total=0;

   for(int r=0;r<size;r++)

   {

      for(int c=0;c<4;c++)

      {

      total+=ar2[r][c];

}

}

return total;

}

其中ar2[r][c]==*(*(ar2+r)+c)

 

6.函数和c风格字符串

将字符串作为参数时意味着传递的是地址。字符串三种表示方式有char数组,“字符串字面值”,被设置为字符串地址的指针。以上三种类型本质都是char指针(char *),这意味着字符串函数原型应将对应形参设置为char *。函数原型例如:

unsigned int c_in_str(const char *str,char ch);unsigned int c_in_str(const char str[],char ch);

C风格字符串和常规char之间的重要区别是:字符串有内置的结束字符,不以空值字符’\0’结束的的char数组只是数组而非字符串。处理字符串中字符的标准方式如下:

while(*str)

{

Statements;str++;

}

 

函数无法返回一个字符串,但可以返回字符串的地址。要创建包含n个字符的字符串需要能够存储n+1(还有一个空字符)个字符的空间。

 

7.函数和结构

与数组不同的是,结构名只是结构的名称,要获取结构的地址需要使用&符号。在函数中结构可以直接使用值传递(结构比较小时),该结构的内容被复制到函数中形参接受处,并且可以将结构返回。(还有方式比如传递结构的地址,使用指针访问结构的内容,按引用传递)。

 

当使用传递结构的地址的方式时,调用函数时,将结构的地址(&pplace)传递给它。将形参声明为指向polar的指针,即polar*类型。使用const类型修饰符。应用->间接成员运算符。

 

8.string字符串数组/array与函数

函数原型声明如void display (const string sa[],int n); 字符串数组声明如string list[5];

声明一个std::array<double,4> expenses;如果调用函数不需要修改expenses的内容那么按值传递如show(expenses);此时函数声明为 void show(std::array<double,4>da);在函数中也直接使用array名+索引的方式进行操作。如果需要修改expenses内容那么需要引用传递,如fill(&expenses),此时函数声明为:void fill(std::array<double,4> *pa);函数中使用(*pa)[i]来操作。

 

9.递归

给出一个程序,P239.

#include<iostream>                              void countdown(int)

void countdown(int n);                            {using namespace std;

int main()                                        cout<<”counting down” <<n<<end;                                               
countdown(4);                                    if(n>0)

return 0;                                         {   coutdown(n-1);}

}                                                cout<<n<<”kaboom”;

                                                }

 

每一次调用countdown(n)都会递归调用,先输出4,3,2,1,0后得到一个结果,再将该结果返回countdown(1)调用断点处,从而走出if,输出最后的语句1,再将该结果返回countdown(2)调用处,从而走出if,输出最后的2,依次执行。内在原理在于:函数调用实际上是1.中断当前的执行序列,2.跳转到函数中执行,3函数执行完毕,4返回中断处执行。注意counting down 的阶段和kaboom阶段的相同层级,n 的地址相同。

 

10.函数指针

函数名(后面不跟括号和参数)表示函数地址。即若think()是一个函数那么将think(就是其地址)传递给另一个函数,使得在其内部可以使用think函数。区分地址和返回值,把think()传递给另一个函数就表示返回值了,即另一个函数调用think()函数然后将think()的返回值传递给调用函数。

 

函数指针:声明函数指针也必须指定指针指向的函数类型,包括返回类型和参数列表。假设该函数原型如下:double pam(int)。那么正确的指针类型声明如下double (*pt)(int);相当于用指针代替函数原型的函数名。这意味着指针pt指向一个函数,它有一个参数返回double类型。必须要用括号将(*pt)括起来,否则double *pt(int);意味着pt是一个返回指针的函数。意义则大相径庭。当声明函数指针后,将相应类型函数的地址赋值给它,即pf=pam;

注意:赋值时,函数指针类型和函数参数类型,函数指针指向的函数返回类型与函数返回类型必须一致才可以进行赋值。

应用举例:void estimate(int lines, double (*pt)(int));函数的第二个参数是函数指针,它指向的函数接受一个int参数,并且返回一个double值。调用时estimate(50,pam);

 

使用指针来调用函数:(*pt)相当于pam即函数名。因此可以进行直接使用,如pf=pam;double x=pam(4);double y=(*pt)(5);实际上c++也允许像使用函数名一样使用指针pt。即double y=pt(5);

 

函数指针数组:

const double * f1(const double arr[],int n);

const double * f2(const double [],int);

const double * f3(const double *,int); 接着声明一个函数指针指向其中之一,假设指针pa,那么const double *(*p1)(const double arr[],int n); 可同时进行初始化,后面=f1

用auto简单,即如auto p2=f2;

 

其中*p1(av,3)和p2(av,3)都调用指向的函数(f1和f2),这两个函数指针指向的函数返回值是const double *类型,即地址,因此再次使用*可以获得其值,即*(*p1)(av,3),*p2(av,3)。可见如果声明一个函数指针数组那么可以使用for循环来逐个调用函数。如下:

Const double *(*pa[3])(const double*,int)={f1,f2,f3};

注意:运算符[]的优先级高于*,因此*pa[3]意味着是一个包含三个指针的数组。每个指针指的是返回类型为const double *,接受一个函数和整型作为参数的函数。

 

如何调用:

const double *px=pa[0](av,3);   const double *py=(*pb[1])(av,3);

要获得指向的double值,使用运算符*

double x=*pa[0](av,3);  double y=*(*pb[1])(av,3);

还有指向函数指针数组的指针,很复杂。。。参照P246.

#include<iostream>
#include<cctype>
//++++++++++++++++++++++++1++++++++++++++++++++++++++++++
using namespace std;
double average(double,double);
int main()
{
cout<<"enter two non-zero number"<<endl;
double x,y;
double *z=new double [10];
int i=0;
cin>>x>>y;
   while(x!=0&&y!=0)
   {
   z[i]=average(x,y);
   i++;
   cout<<"enter again"<<endl;
   cin>>x>>y;
   }
for(int j=0;j<i;j++)
{
cout<<z[j]<<endl;
}
delete []z;
return 0;
}

double average(double a,double b)
{
double ave;
ave=(2.0*a*b)/(a+b);
return ave;
}
//+++++++++++++++++++++2+++++++++++++++++++++++++++++++++++
using namespace std;
int get_num(double a1[],int);
void cout_num(double a2[],int);
double ave(double a3[],int);
int main()
{
int num;
double *scores=new double [10];
num=get_num(scores,10);
cout_num(scores,num);
ave(scores,num);
return 0;
}

int get_num(double a1[],int limit)
{
double temp;
int i;
for(i=0;i<limit;i++)
{
   cout<<"enter value"<<(i+1)<<endl;
   cin>>temp;
   if(!cin)
   {
      cin.clear();
      while(cin.get()!='\n')
      {
         continue;
      }
      cout<<"bad input!"<<endl;
      break;
   }
   else if(temp<0)
   {
      break;
   }
   a1[i]=temp;
      
}
return i;
}

void cout_num(double a2[],int number)
{
for(int i=0;i<number;i++)
{
cout<<a2[i]<<" ";
}
}

double ave(double a3[],int number)
{
double sum=0;
for(int i=0;i<number;i++)
{
sum=sum+a3[i];
}
cout<<sum/number;
return sum/number;
}
//+++++++++++++++++++++++++3++++++++++++++++++++++++++++++++++++
struct box
{
   char maker[40];
   float height;
   float width;
   float length;
   float volume;
};
void a(box b1);
float b(box *b2);
int main()
{
box bb=
{
"hahahahah",
1.1,
3.0,
2.0,
1.0
};
a(bb);
b(&bb);
return 0;
}

void a(box b1)
{
cout<<b1.height<<endl;
cout<<b1.width<<endl;
cout<<b1.length<<endl;
cout<<b1.volume<<endl;
cout<<b1.maker<<endl;
}

float b(box *b2)
{
b2->volume=(b2->height)*(b2->width)*(b2->length);
cout<<b2->volume<<endl;
return b2->volume;
}
//++++++++++++++++++++++++++++++4++++++++++++++++++++++++++++++++
long double pro(unsigned int ,unsigned);
int main()
{
unsigned int num_1=57;
unsigned int pick_1=5;
unsigned int num_2=27;
unsigned int pick_2=1;
long double a=pro(num_1,pick_1);
long double b=pro(num_2,pick_2);
cout<<"you have one chance in "<<a<<endl;
cout<<"you have one chance in "<<b<<endl;
cout<<"you have one chance in all of"<< a*b<<endl;
return 0;
}


long double pro(unsigned int numbers,unsigned int picks)
{
   unsigned int n=0;
   long double result=1;
   unsigned int p;
   for(n=numbers,p=picks;p>0;p--,n--)
   {
      result=result*n/p;
   
   }
return result;
}
//++++++++++++++++++++++++++++5+++++++++++++++++++++++++++++++++++
long int multiple(int n);
int main()
{
int a;
cout<<"enter your num"<<endl;
while (cin>>a)
{
   cout<<"your multiple is "<<multiple(a)<<endl;
}
return 0;
}


long int multiple(int size)
{
   if(size==1||size==0)
      return 1;
   else
      return size*multiple(size-1);

}
//++++++++++++++++++++++++6+++++++++++++++++++++++++++++++++++++++
int fill_array(double arr[],int);
void show_array(double arr[],int);
void reverse_array(double arr[],int);
const int number=20;
int main()
{
double ar[number];
int input_num;
input_num=fill_array(ar,number);
show_array(ar,input_num);
reverse_array(ar,input_num);
show_array(ar,input_num);
}

int fill_array(double arr[],int size)
{
int i=0;
double ch;
while(cin>>ch)
{
if(!cin)
{
   cin.clear();
   while(cin.get()!='\n')
   {
      continue;
   }
   cout<<"bad input"<<endl;
   break;
}
else if(isdigit(ch))
   {
   cout<<"you have not entered a number"<<endl;
   break;
   }
arr[i]=ch;
i++;
cout<<"you have entered "<<i<<" number "<<endl;
}
return i;
}

void show_array(double arr[],int size) 
{
   for(int i=0;i<size;i++)
   {
     cout<<arr[i]<<endl;
   }
}

void reverse_array(double arr[],int size)
{
for(int i=1,j=size-2;i<j;i++,j--)
{
   double temp;
   temp=arr[i];
   arr[i]=arr[j];
   arr[j]=temp;
}
}
//++++++++++++++++++++++++++++7+++++++++++++++++++++++++++++
double *get_arr(double *begin,double *end);
void cout_arr(const double *begin,const double *end);
void revalue(double *begin,double *end,int n);
const int size=20;
int main()
{
double arr[size];
double *p=get_arr(arr,arr+size);
cout_arr(arr,p);
revalue(arr,p,3);
cout_arr(arr,p);
return 0;
}


double *get_arr(double *begin,double *end)
{
int count;
double *pt;
double a;
for(pt=begin;pt!=end;pt++)
{
cin>>a;
if(!cin||isalpha(a))
{
   cin.clear();
   while(cin.get()!='\n')
   {
      continue;
   }
   cout<<"bad input"<<endl;
   break;
}
else
{
(*pt)=a;
count++;
cout<<"you have entered "<<count<<" number "<<endl;
}
}
return pt;
}
void cout_arr(const double *begin,const double *end)
{
const double *ps;
for(ps=begin;ps!=end;ps++)
{
   cout<<(*ps)<<endl;
}
}

void revalue(double *begin,double *end,int n)
{
double *pp;
for(pp=begin;pp!=end;pp++)
{
   (*pp)*=n;
   cout<<(*pp)<<endl;
}
}
//++++++++++++++++++++++++++++++++++++8+++++++++++++++++++++++++++++++++
const int Seasons = 4;
const char *Snames[Seasons] = { "Spring", "Summer", "Fall", "Winter" };
struct cost
{
    double expenses[Seasons];
};
void fill(double *pa)
{
    for (int i = 0; i < Seasons; i++)
    {
        cout << "Enter " << Snames[i] << " expenses: ";
        cin >> pa[i];
    }
}
void show(double *pa)
{
    double total = 0.0;
    cout << "\nEXPENSES\n";
    for (int i = 0; i < Seasons; i++)
    {
        cout << Snames[i] << ": $" << pa[i] << endl;
        total += pa[i];
    }
    cout << "Total Expenses: $" << total << endl;
}
void fill(struct cost *pCost)
{
    for (int i = 0; i < Seasons; i++)
    {
        cout << "Enter " << Snames[i] << " expenses: ";
        cin >> pCost->expenses[i];
    }
}
void show(struct cost *pCost)
{
    double total = 0.0;
    cout << "\nEXPENSE\n";
    for (int i = 0; i < Seasons; i++)
    {
        cout << Snames[i] << ": $" << pCost->expenses[i] << endl;
        total += pCost->expenses[i];
    }
    cout << "Total Expense: $" << total << endl;
}
int main()
{
    // situation a
    cout << "Situation a: " << endl;
    double pa[Seasons] = { 0 };
    fill(pa);
    show(pa);

    // situation b
    cout << endl << endl;
    cout << "Situation b: " << endl;
    struct cost *pCost = new struct cost;
    fill(pCost);
    show(pCost);
    delete pCost;
}


//++++++++++++++++++++++++++++++++++++++++++++++9+++++++++++++++++++++++++++++++++++++++
const int SLEN = 30;
struct student
{
    char fullname[SLEN];
    char hobby[SLEN];
    int ooplevel;
};
int getinfo(student pa[], int n)
{
    int enter = 0;
    for (int i = 0; i < n; i++)
    {
        cout << "Enter the infomation of student #" << i+1 << endl;
        cout << "Enter full name (blank line to quit): ";
        cin.getline(pa[i].fullname, SLEN);
        cout << "Enter hobby: ";
        cin.getline(pa[i].hobby, SLEN);
        cout << "Enter ooplevel: ";
        cin >> pa[i].ooplevel;
        enter++;
    }
    return enter;
}
void display1(student st)
{
    cout << "Using display1(student st): " << endl;
    cout << "Full name: " << st.fullname << endl;
    cout << "Hobby: " << st.hobby << endl;
    cout << "Ooplevel: " << st.ooplevel << endl;
}
void display2(const student *st)
{
    cout << "Using display2(const student *st)" << endl;
    cout << "Full name: " << st->fullname << endl;
    cout << "Hobby: " << st->hobby << endl;
    cout << "Ooplevel: " << st->ooplevel << endl;
}
void display3(const student pa[], int n)
{
    cout << "Using display3(const student pa[], int n)" << endl;;
    for (int i = 0; i < n; i++)
    {
        cout << "Infomation of student #" << i + 1 << ": " << endl;
        cout << "Full name: " << pa[i].fullname << endl;
        cout << "Hobby: " << pa[i].hobby << endl;
        cout << "Ooplevel: " << pa[i].ooplevel << endl;
    }
}
int main()
{
    cout << "Enter class size: ";
    int class_size;
    cin >> class_size;
    while(cin.get()!='\n')
        continue;
    student *ptr_stu = new student[class_size];
    int entered = getinfo(ptr_stu, class_size);
    for (int i = 0; i < entered; i++)
    {
        display1(ptr_stu[i]);
        display2(&ptr_stu[i]);
    }
    display3(ptr_stu, entered);

    delete[] ptr_stu;
    cout << "Done.\n";
}
//++++++++++++++++++++++++++10++++++++++++++++++++++++++++++++++++++++
double add(double x, double y)
{
    return x + y;
}
double mul(double x, double y)
{
    return x * y;
}
double calculate(double x, double y, double(*fun)(double, double))
{
    return fun(x, y);
}
void p7_10(void)
{
    double x = 0.0;
    double y = 0.0;

    cout << "Enter two double number: ";
    while (cin >> x >> y)
    {
        cout << "Call add, the result of " << x << " and " << y << " is " << calculate(x, y, add) << endl;
        cout << "Call mul, the result of " << x << " abd " << y << " is " << calculate(x, y, mul) << endl;

        cout << "Enter next two double number: ";
    }
}


int main(int argc, char **argv)
{
    p7_10();

    while (getchar());

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值