c++ primer plus 第六版 第七章重點內容總結 以及 程式設計題答案
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;
}