1. 程式人生 > >c++ primer plus(第六版) 第四章重點內容總結 以及手碼練習題答案

c++ primer plus(第六版) 第四章重點內容總結 以及手碼練習題答案

1.陣列

可儲存多個同類型的的值。宣告時候 型別 陣列名【元素個數】。其中元素個數必須是整型常數或者const值,常量表達式。c++陣列都是從0開始編號。最後一個元素的索引比陣列長度小一。貌似編譯器不會檢查使用的下表是否有效。sizeof用於陣列名時得到整個陣列中的位元組數,用於陣列元素時,得到元素的長度。(單位都是位元組)。

關於陣列初始化,只有在定義陣列時才能使用花括號初始化(列表初始化)且不能將一個數組賦給另一個數組。使用花括號只能在一步定義時使用,而不可先聲明後賦值。且花括號可以省略等號,禁止元素的縮窄轉換。(浮點數到整型)但使用索引下標賦值是可以分開進行的。若只對陣列一部分元素顯示初始化,那麼其餘元素被預設為0.。若【】內為空,編譯器會計算陣列個數。但是不推薦這種做法。指明:sizeof運算子指出整個陣列的長度(位元組bytes),strlen只計算可見的字元而不把空字元包含在內。在陣列中只能處理到空字元為止的字串。strlen(charr)接受一個c風格字串

2.字串

c++處理字串有兩種方式,第一個是c-風格字串,第二個基於string類字串。

其中c風格字串以空字元結尾,‘\0’,因此儲存字串的陣列長度要+1.如char cat【8】 = {'f','a','t','e','s','s','a','\0'};。還有一種方法簡便可行,使用字串常量初始化/字串字面值進行初始化(使用雙引號)。 如 char bird[11]="Mr.  Cheeps".用引號括起來的字串隱式地包括結尾地空字元因此不必顯示地包含。一定注意單引號雙引號的區別,如“s”是包含‘\0’地字串,賦給變數其實是賦值地記憶體地址,賦給陣列才是字串。而‘s’才是字元,可以賦給變數。C風格字串可以使用strcpy(charr1,charr2)(把charr2賦值給charr1);strcat(charr1,charr2)(把charr2新增到charr1末尾)

3.cin的用法

cin使用空白(空格,製表符,換行符)來確定輸入字串的結束位置。意味著如果你輸入兩個單詞,cin只讀取第一個將其放在陣列中在結尾新增'\0'空字元。而另一個留在輸入佇列中。因此istream提供面向行讀入的getline() ,如cin.getline(name,20)只能讀取19個字元cin.getline(name1,20).getline(name2,20)可以連續讀入兩行。get(),若get(name,20)兩次那麼第一次後取到正常字串儲存,然而換行符留在輸入佇列,下一次則只能讀取換行符以為到達行尾而讀不到任何資訊。兩句中間可加入cin.get()來讀取下一個字元,或者cin.get(name,20).get()。兩者均是讀取一行輸入直到到達換行符,但getline()丟棄換行符,將其替代為空字元存入陣列,get則將換行符保留在輸入佇列中。

當使用cin和cin.getline/cin.get混用讀入字串和數字的時候,應該(cin>>  ).get() 後再使用getline()/cin.get(),因為cin後存在一個換行符,用cin.get讀取後,之後的getline才能正常運作。否則換行符留在輸入佇列中會使得之後的getline讀取不到任何內容。

4.string類

須在標頭檔案中包含#include<string>,且string類位於名稱空間std中,因此using namespace std;/std::string是需要的。string物件可以將變數宣告為一個簡單變數而不是陣列,類設計使得程式能夠自動處理string的大小。且string類可以接受列表初始化。與陣列不同,可以將一個string物件賦值給另一個string物件。string物件可以使用+將兩個物件合併/+=拼接字串將字串加到string物件的末尾(也可以與c風格字串(“hahaha”)混+)。str.size()返回string類物件的字元數。getline(cin,str)從輸入流中讀取一行。

5。結構體

建立結構有兩步,定義結構描述,然後按照描述建立結構變數。結構描述如

struct inflatable

{char name[20];float volume;double price;}; 然後建立結構變數 如,inflatable hat;(c++允許省略關鍵字struct)。呼叫類成員如:hat.name;是一個char 型 字串。初始化舉例:inflatable guest={"selina",1.99,5.97865};c++不提倡使用外部變數,但提倡使用外部結構宣告。其中“=”是可選的。若大括號內未包含任何東西,那麼各個成員都被設定成為0.且不允許縮窄轉換。注意結構也是支援使用string類成員的,如inflatable haha{string std::name;float volume;double price;};另外可以使用成員賦值方式將一個結構賦值給另一個同一型別的結構,這樣結構的每個成員都被設定成為另一個結構中相應成員的值。即使成員是陣列。

另可以同時完成定義結構和建立結構變數的工作,如:

struct perks{int key_number;char car[12];}mr_sminth,ms_jones;是緊接著進行初始化。甚至可以初始化以這種方式建立的變數:

struct perks{int key_number;char car[12];}mr_glitz={7,"packard"};

6.結構陣列

即元素為結構的陣列。如inflatable gifts[100];意為一個包含100個inflatable結構的陣列。gifts本身是一個數組,而其元素為結構,即可以使用gift[0].name來呼叫結構成員。初始化:inflatable gift[2]={{"haha",2.34,345.8},{"hehe",23.4,5.87}};

7.共用體

句法與結構體相似,含義不同,且只能同時儲存其中的一種型別。union one4all{int int_val;long long_val;double double_val};可以使用one4all儲存int,long或者double。共同體每次只能儲存一個值,當資料使用兩種或者多種格式時可以使用共同體節省空間。常用於嵌入式程式設計,作業系統資料結構或者硬體資料結構。

8. 列舉

enum工具提供另一種建立符號常量的工具,代替const,並且可以建立新的型別。enum spectrum{red,orange,yellow};兩個重點,第一spectrum為新型別的名稱,為列舉型別。第二,red,orange,yellow等作為符號常量,它們對應整數值0~2,這些常量叫做列舉量。spectrum band = red;是正確的。列舉量是整型,則可被提升為int型別(即相當於列舉量的索引),但是int型別不能被轉變為列舉型別。另外如果int值是有效的(即在列舉量的索引中),可以通過強制轉換如band=spectrum(3);賦值給列舉變數。可以使用賦值運算子來顯示設定列舉量的值 如enum bits={one=1,two=2,four=4,eight=8};指定的值必須為整數,也可以僅僅顯示定義部分列舉量的值。enum bigstep{first,second,=100,third};fisrt=0,未被初始化的列舉量將比前面大1,third為101。也可建立多個值相同的列舉量enum={zero,null=0,one,numero_uno=1};。需要了解每一個列舉的取值範圍,從而通過強制型別轉換將取值範圍中的任何整數值賦給列舉變數(即使列舉值中沒有它)。上限:找到大於列舉量最大值的,最小的2的冪再減去1,即如最大列舉值101,那麼在2的冪中比這個最大值大的最小值是128,因此-1=127. 下限:如果列舉值的最小值不小於0,那麼下限為0.如果否則上限規則尋找,如最小為-6,那麼就是-8+1=-7.不論尋找上限還是下限,均是選擇趨向於0軸的方向。

9.指標

常規變數使用&符號獲取地址。指標用於儲存值的地址,因此指標名錶示的是地址,利用*加之指標之上可以得到該地址處儲存的值。如mainly是一個指標表示一個地址,而*mainly表示儲存在該地址處的值。int a=4;int * b=&a;其中a和*b是一樣的,都是4,&a和b是一樣的地址。可以像操作a一樣操作*b,賦值等等,即修改指向的值a。b是指向int型別的指標或者int*型。可以說b是地址,而*b是int型別不是指標。*號位置引人而異,但宣告時每一個指標必須要使用一個*。如上在宣告語句中初始化指標,被初始化的是指標而不是它指向的值。也就是說將b的值設定為&a。

一個對於以後跟蹤bug極其隱匿的錯誤,如long *a;*a=233;這是錯誤的指標在使用前未被初始化。注意:一定要在對指標應用解除引用運算子*之前,將指標初始化為一個確定的,適當的地址!!!且指標不是整型,不能簡單粗暴的將整數賦值給指標。而應將數字轉化為適當的地址型別。如int *a;a=(int *)0xB000000

10.使用new分配記憶體

變數是在編譯時分配的有名稱的記憶體,而指標是為了在執行階段分配未命名的記憶體以儲存值。這種情況下只能通過指標來訪問記憶體。如 int *pn=new int,new int告訴程式需要適合儲存int 的記憶體。new運算子根據型別來確定需要多少位元組的記憶體,將該記憶體地址返回,並將該地址賦值給一個指標。這種方法與將變數的地址賦給指標的方法int a=4;int * b=&a相比,都是將一個int型變數的地址賦值給指標,但在第二種情況下可以使用變數名a來訪問該int值,而new方法則只能使用指標進行訪問。此時我們說pn指標指向指向一個數據物件(為資料項分配的記憶體塊)。對於指標,new分配的記憶體塊通常與常規變數宣告分配的記憶體塊不同,變數nights和pd的值都儲存在被稱為棧的記憶體區域中,而new從被稱為堆/自由儲存區的記憶體區域分配記憶體。必須配對使用delete和new的,防止記憶體洩漏,即被分配的記憶體再也無法使用。int *ps = new int;delete ps;這將釋放ps所指向的記憶體。另外delete不能用於釋放宣告變數所獲得的記憶體。

11.使用new建立動態陣列

在編譯時給陣列分配記憶體被稱為靜態聯編,必須指定陣列的長度,不論最終是否用到這塊記憶體,程式已經為他分配記憶體空間了。使用new時,如果在執行階段需要陣列則建立它,還可以在程式執行時選擇陣列的長度,這被成為動態聯編。用該種方式建立的時動態陣列。使用new建立動態陣列,如int *psome=new int 【10】;new返回第一個元素的地址並且賦值給該指標。使用後,用運用delete 【】 psome;釋放整個陣列。注意:當new使用了【】,那麼delete也應該使用【】。對空指標應用delete是安全的。因為psome指向陣列的第一個元素,因此*psome是第一個元素的值,訪問其他元素時只需將指標當作陣列名來使用即可,即psome【0】,psome【1】.。。將psome=psome+1的結果相當於將指向第一個元素的指標加1.

12.指標/陣列/指標算術

將整數變數+1,其值也+1,但將指標變數+1後增加的量等於它指向的型別佔用的位元組數。如將指向double的指標+1,如果系統對double使用8個位元組儲存,則數值+8。將指向short指標+1後,如果系統對short使用2個位元組儲存,則指標值+2.多數情況下,c++將陣列名解釋為第一個元素的地址。用陣列表示法時等同於:arrayname【i】 becomes *(arrayname+i)。如果使用的是指標等同於:pointername【i】becomes *(pointername+i)。但是陣列和指標表達的區別是,指標可以修改,但陣列名是常量,另一個區別是,對陣列應用sizeof得到是整個陣列的長度,而對指標應用sizeof得到的是指標的長度。P109頁陣列的地址部分:對陣列名應用&得到的是整個陣列的地址。如short tell【10】;cout<<tell<<endl;cout<<&tell<<endl;此刻tell相當於一個指標==&tell【0】是一個2位元組記憶體塊的地址,得到第0個元素的地址。&tell得到的是一個20位元組記憶體塊的地址。將表示式tell+1將地址值+2,而將&tell+2,則是將地址值+20。當兩個指標指向同一個陣列時,可以對兩個指標做差求得兩元素間隔。對於指標和陣列名既可以使用指標表示法也可以使用陣列表示法。如int *pt=new int【10】;*pt=5;pt【0】=6;int coats【10】;*(coats+4)=12

另注意,在cout和c++表示式中,char陣列名,char指標,以及用引號括起來的字串常量都被解釋為字串的第一個地址,cout從該字元開始列印直到遇到空字元為止。並不要使用字串常量或未被齒初始化的的指標來接受輸入cin。對於輸入來說,使用陣列較好,使用指標(對字面值使用const型別)並不合適,因為有些編譯器將字串字面值視為只讀常量,試圖修改則將在執行階段錯誤。且有些編譯器只使用字串字面值的一個副本來表示該程式中所有的字面值。在將字串讀入程式時應使用已經分配的記憶體地址。該地址可以是陣列名,也可以是使用new初始化過的指標。

一般來說,給cout提供一個指標將列印地址,但如果指標型別為char*則cout將顯示指向的字串。此時若要求顯示字串地址,則必須強制轉換為另一種指標型別,如int* 使用char animal[20]="bear" 和const char *bird="bear",cout<<animal<<bird<<endl;結果一樣。字串字面值是常量使用const意味著可以使用bird訪問它,但不能修改它。

應該使用strcpy和strncpy而不是賦值運算子將字串賦給陣列。其中strncpy可接受第三個引數--要複製的最大字元數,但如果該函式在到達字串結尾之前目標記憶體已經用完則它將不會新增空字元。

13.使用new建立動態結構

inflatable *ps=new inflatable;將足以儲存inflatable結構的一塊可用記憶體的地址賦給ps。但建立動態結構時不能將成員運算子.用於結構名,因為這種結構沒有名稱只有地址。c++專門為該情況提供了運算子->。可用於指向結構的指標,如ps指向inflatable結構,那麼ps->price是被指向的結構的price成員。

如struct things={int good;int bad;};things grubnose={3,453};things *pt=&grubnose,grubnose.good;=pt->good=3。

還可以用(*ps).price,因為*ps相當於結構本身。總之如果結構識別符號是結構名則使用句點運算子,如果識別符號是指向結構的指標則使用箭頭運算子。可參照P116頁程式碼段。

14.自動儲存,靜態儲存和動態儲存

c++的三種管理資料記憶體的方式(c++11新增了第四種類型執行緒儲存)。

自動儲存:在函式內部定義的常規變數使用自動儲存空間也叫自動變數。在所屬函式被呼叫時自動產生,在該函式結束時消亡。自動變數是區域性變數,作用域為包含它的程式碼塊(被包含在花括號中的一段程式碼)。自動變數通常儲存在棧中,在執行程式碼塊時其中的變數依次進入棧中,而在離開程式碼塊時將按照相反的順序釋放這些變數。即後進先出。程式執行過程中,棧將不斷地放大或者縮小。

靜態儲存:整個程式執行期間都存在的儲存方式,讓變數成為靜態的方式有兩種:一種是在函式外定義它,另一種是在宣告變數時使用關鍵字static:static double fee=56.50;自由儲存和靜態儲存的的關鍵在於:這些方法嚴格限制了變數的壽命,靜態變數可能存在與程式的整個宣告週期,自動變數只在特定函式被執行時存在。

動態儲存:new和delete運算子管理了一個記憶體池,被稱為自由儲存空間或者堆。該記憶體池用於靜態變數和自動變數的記憶體是分開的。該兩個運算子可以在一個函式中分配記憶體而在另一個函式中釋放它。因此資料的生命週期不完全受程式或者函式的生存時間控制。

棧,堆和記憶體洩漏:若使用new在自由儲存空間或者堆上建立變數後沒有呼叫delete,即使包含指標的記憶體由於作用於規則和物件生命週期的原因而被釋放,在自由儲存空間上動態分配的變數或者結構也將繼續存在。實際上將無法訪問自由儲存空間中的結構,因為指向這些記憶體的指標無效。則導致記憶體洩漏。被洩漏的記憶體將在程式的整個生命週期內都不可使用。這些記憶體被分配出去但無法收回。避免的方法是,在自由儲存空間上動態分配記憶體隨後就應該釋放它。參照P119頁程式碼。

15.模板類vector

是一種動態陣列,可在執行階段設定vector 物件的長度並可在末尾新增新資料還可在中間插入新資料。(其實還是用new和delete完成,不過是自動的)。使用前須包含標頭檔案#include<vector>,vector 在名稱空間std中因此需要使用using編譯指令,或者std::vector。宣告舉例:vector <int> vi;vector <double> vd(n);其中n既可以是整型常量也可以是整型變數。vector功能比陣列強大但是效率比較低。

16.模板類array

位於std空間中,與陣列一樣,長度固定且在靜態記憶體分配而非在自由儲存區。其效率與陣列一樣,更方便更安全,需要#include<array>。宣告舉例:array<int ,5>ai;array <double,4>ad={1.2,3.4,5.6,3.5},與vector不同的是元素個數不能是變數只能是常量。可以將一個array物件賦值給另一個array物件。對於陣列必須逐元素複製。

#include<string>
#include<iostream>
#include<cstring>
#include<array>
//+++++++++++1+++++++++++++++++++++++++++++++++++++
int main()
{
using namespace std;
const int SIZE=15;
string a,b;
char c;
int d;
cout<<"What is your first name?"<<endl;
getline(cin,a);
cout<<"What is your last name"<<endl;
getline(cin,b);
cout<<"What letter grade do you deserve?"<<endl;
cin>>c;
cout<<"What is your age?"<<endl;
cin>>d;
cout<<"Name:"<<b<<","<<a<<endl;
cout<<"Grade:"<<char(c+1)<<endl;
cout<<"Age:"<<d<<endl;
return 0;
}
//++++++++++++++++++++++2++++++++++++++++++++++++++++++++

int main()
{
using namespace std;
string a,b,c;
cout<<"Enter your name:\n";
getline(cin,a);
cout<<"Enter your favorite dessert:\n";
getline(cin,b);
cout<<"I have some delicious"<<b;
cout<<"for you"<<a<<".\n";
return 0;
}
//+++++++++++++++++++++++++++3++++++++++++++++++++++++++++++++
const int SIZE=20;
int main()
{
using namespace std;
char a[SIZE];
char b[SIZE];
char c[2*SIZE+1];
cout<<"Enter your first name"<<endl;
cin.getline(a,SIZE);
cout<<"Enter your last name"<<endl;
cin.getline(b,SIZE);
strncpy(c,b,SIZE);
strcat(c,",");
strncat(c,a,SIZE);
c[2*SIZE]='\0';
cout<<"Here\'s the information in a single string:"<<c<<endl;
return 0;
}
//++++++++++++++++++++++++++++++4++++++++++++++++++++++++++++++++

int main()
{
using namespace std;
string a,b;
cout<<"Enter your first name:";
getline(cin,a);
cout<<"Enter your last name:";
getline(cin,b);
cout<<"Here\'s the information in single string:"<<b<<", "<<a<<endl;
return 0;
}


//+++++++++++++++++++++++++++++++5+++++++++++++++++++++++++++++++++++++
struct CandyBar
{
char pinpai[20];
double weights;
int calolis;
}snack=
{
"Mocha Munch",
2.3,
350
};
int main()
{
using namespace std;
cout<<snack.pinpai<<"\n"
    <<snack.weights<<"\n"
    <<snack.calolis<<endl;
return 0;
}
//+++++++++++++++++++++++++++++6++++++++++++++++++++++++++++++++++++++++
struct CandyBar
{
char name[20];
double weights;
double heights;
};
int main()
{
using namespace std;
CandyBar nba[3]
{
{"james",23.00,78.00},
{"curry",56.00,56.00},
{"durant",34.00,90.00}
};
for(int i=0;i<3;i++)
{
cout<<nba[i].name<<" "<<nba[i].weights<<" "<<nba[i].heights<<endl;
}
return 0;
}
//+++++++++++++++++++++++7+++++++++++++++++++++++++++++++++++++++++++++++++
using namespace std;
struct pizza
{
string company;
double zhijing;
double weights;
};
int main()
{
using namespace std;
pizza m;
cout<<"Your company"<<endl;
getline(cin,m.company);
cout<<"is"<<m.company;
cout<<"Your zhijing"<<endl;
cin>>m.zhijing;
cout<<"is"<<m.zhijing;
cout<<"Your weights"<<endl;
cin>>m.weights;
cout<<"is "<<m.weights;
return 0;
}

//+++++++++++++++++++++++8+++++++++++++++++++++++++++++++++++++++++++++++++
using namespace std;
struct pizza
{
string company;
double zhijing;
double weights;
};
int main()
{
pizza *ps=new pizza;
cout<<"Your company"<<endl;
getline(cin,ps->company);
cout<<"is"<<ps->company<<endl;;
cout<<"Your zhijing"<<endl;
cin>>ps->zhijing;
cout<<"is"<<ps->zhijing<<endl;
cout<<"YOur weights"<<endl;
cin>>ps->weights;
cout<<"is"<<ps->weights<<endl;;
delete ps;
return 0;
}*
//+++++++++++++++++++++++++9+++++++++++++++++++++++++++++++++++++++++++++++++++
using namespace std;
struct CandyBar
{
char name[20];
double weights;
double heights;
};
int main()
{
CandyBar *ps=new CandyBar[3]{
   {"haha",78.00,56.78},
   {"dabaidu",56.90,45.90},
   {"memeda",89.34,56.90}
};
for(int i=0;i<3;i++)
{
cout<<ps[i].name<<ps[i].weights<<ps[i].heights<<endl;
}
delete []ps;
return 0;
}
//+++++++++++++++++++++++10++++++++++++++++++++++++++++++++++++++++++++++++++++
int main()
{
using namespace std;
array<double,3> scores;
cout<<"fisrt score:";
cin>>scores[0];
cout<<"second score:";
cin>>scores[1];
cout<<"third score:";
cin>>scores[2];
double aver=(scores[0]+scores[1]+scores[2])/3;
cout<<"three aver is"<<aver;
return 0;
}