C++語言程式設計筆記- 第6章- 陣列、指標與字串 - IT人

文章推薦指數: 80 %
投票人數:10人

文章目錄第6章陣列、指標與字串6.1 陣列6.1.1 陣列的宣告與使用6.1.2 陣列的儲存與初始化6.1.3 陣列作為函式引數6.1.4 物件陣列6.2 指標6.2.1 記憶體 ... Togglenavigation IT人 IT人 C++語言程式設計筆記-第6章-陣列、指標與字串 oddmarmot發表於 2020-12-05 C++ 文章目錄 第6章陣列、指標與字串6.1陣列6.1.1陣列的宣告與使用6.1.2陣列的儲存與初始化6.1.3陣列作為函式引數6.1.4物件陣列 6.2指標6.2.1記憶體空間的訪問方式6.2.2指標變數的宣告6.2.3與地址相關的運算“`*`”和“`&`”6.2.4指標的賦值6.2.5指標運算6.2.6用指標處理陣列元素6.2.7指標陣列6.2.8用陣列作為函式引數6.2.9指標型函式6.2.10指向函式的指標(函式指標)6.2.11物件指標6.2.11.1物件指標的一般概念6.2.11.2this指標6.2.11.3指向類的非靜態成員的指標6.2.11.4指向類的靜態成員的指標 6.3動態記憶體分配6.4用vector建立陣列物件6.5深複製與淺複製6.6字串6.6.1用字元陣列儲存和處理字串6.6.2string類6.6.2.1初始化string物件6.6.2.2讀取和輸出string物件6.6.2.3getline讀取一整行6.6.2.4範圍for語句6.6.2.5empty函式判斷string物件是否為空6.6.2.6size/length函式返回string物件的長度6.6.2.7erase函式刪除子串6.6.2.8substr函式提取子串6.6.2.9find和rfind函式查詢子串6.6.2.10insert函式插入字串6.6.2.11replace函式代替子串6.6.2.12swap函式調換兩個字串6.6.2.13c_str函式轉換成C風格字串6.6.2.14string類的操作符 第6章陣列、指標與字串 C++從C繼承來的一個重要特性就是可以直接使用地址來訪問記憶體,指標變數時實現這一特徵的重要資料型別。

應用指標,可以方便地處理連續存放的大量資料,以較低的代價實現函式間的大量資料共享,靈活地實現動態記憶體分配。

字元資料可以用來表示字串,這是從C語言繼承的有效方法,但是從物件導向的觀點和安全性的角度來看,用字元資料表示的字串有不足之處,因此標準C++類庫中提供了string類,這是通過類庫來擴充套件資料型別的一個很好的範例。

6.1陣列 陣列是具有一定順序關係的若干物件的集合體,組成陣列的物件稱為該陣列的元素。

6.1.1陣列的宣告與使用 陣列型別宣告的一般形式: 資料型別識別符號[常量表示式1][常量表示式2]...; //陣列型別宣告的一般形式 陣列中同型別資料的型別由“資料型別”給出,這個“資料型別”可以是整形、浮點型等基本型別,也可以是結構體、類等使用者自定義型別。

"識別符號"指定陣列名稱。

“[常量表示式1][常量表示式2]...”稱為陣列的界,必須是在編譯時就可求出的常量表示式,其值必須為正整數。

有n個下標的陣列稱為n維陣列。

陣列的下標的個數稱為陣列的維數。

二維陣列被當作一維陣列的陣列,更高維陣列也如此。

如果發生了陣列越界(使用陣列時下標值超過了n),執行時有時會得到提示,有時會沒有任何提示! 6.1.2陣列的儲存與初始化 陣列元素在記憶體中是順序、連續儲存的。

陣列元素的初始化(宣告陣列時對部分或全部元素賦初值): inta[3]={1,1,1}; inta[]={1,1,1}; //與上一行等價 floatb[5]={1.0,2.0,3.0}; //部分初始化,不能間隔賦初值,元素個數必須給出 intc[][3]={1,2,3,4,5,6}; //多維陣列若給出全部初值,第一維可省略 intc[2][3]={{1,2,3},{4,5,6}}; //與上一行等價 constfloatd[5]={1.0,2.03.0}; //宣告為常量的陣列必須給定初值 6.1.3陣列作為函式引數 使用陣列名傳遞引數時,傳遞的是地址。

實引數組的個數不應少於形引數組的個數。

如果在被調函式中對形引數組元素值進行改變,主調函式中實引數組的相應元素值也會改變。

6.1.4物件陣列 物件陣列的元素是類的物件,具有資料成員和函式成員。

宣告一個一維物件陣列: 類名陣列名[常量表示式]; 陣列物件的初始化過程,實際上就是呼叫建構函式來對每一個元素物件進行初始化。

如果在宣告陣列時給每一個陣列元素指定初始值,在陣列初始化過程中就會呼叫與形參型別相匹配的建構函式。

例如Locationa[2]={Location(1,2),Location(3,4)};在執行時會呼叫與形參型別相匹配的建構函式分別初始化a[0]和a[1]。

如果沒有指定陣列元素的初始值,就會呼叫預設建構函式,例如Locationa[2]={Location(1,2)};在執行時首先呼叫帶形參的建構函式初始化a[0],然後呼叫預設建構函式初始化a[1]。

如果需要建立某個類的物件陣列,在設計類的建構函式時就要充分考慮到陣列元素初始化時的需要:當各元素物件的初值要求為相同的值時,應該在類中定義預設建構函式;當各元素物件的初值要求為不同的值時,需要定義帶形參(無預設值)的建構函式。

當一個陣列中的元素物件被刪除時,系統會呼叫解構函式來完成掃尾工作。

通過物件陣列中元素物件訪問公有成員: 陣列名[下標表示式].成員名 6.2指標 指標是C++從C中繼承過來的重要資料型別,提供了一種較為直接的地址操作手段。

動態記憶體分配和管理離不開指標。

6.2.1記憶體空間的訪問方式 具有靜態生存期的變數在程式開始執行之前就被分配了記憶體空間。

具有動態生存期的變數是在程式執行時,遇到變數宣告語句時被分配記憶體空間的。

有時使用變數名訪問記憶體空間不夠方便,或者根本沒有變數名可用,這時就需要直接用地址來訪問記憶體單元。

6.2.2指標變數的宣告 指標是一種資料型別。

指標變數用於存放記憶體單元地址。

宣告指標的語法形式: 資料型別*識別符號 //宣告指標 比如: int*ptr; //定義一個指向int型別資料的指標變數 指標可以指向各種型別,包括基本型別、陣列(資料元素)、函式、物件、指標。

6.2.3與地址相關的運算“*”和“&” “*”稱為指標運算子,也稱解析,表示獲取指標所指的變數的值,是一元操作符。

“&”稱為取地址運算子,用於得到一個物件的(儲存單元)地址,是一元操作符。

“*”和“&”出現在宣告語句中和執行語句中,其含義是不同的。

作為一元運算子和二元運算子時含義也不同。

(1)在宣告語句中的一元運算子“*”和“&” 一元運算子“*”出現在宣告語句中,在被宣告的變數之前時,表示宣告的是指標。

一元運算子“&”出現在宣告語句中,在被宣告的變數之前時,表示宣告的是引用。

(2)在非宣告語句中的一元運算子“*”和“&” 一元運算子“*”出現在執行語句中,或在宣告語句的初始化表示式中時,表示訪問指標所指物件的內容。

一元運算子“&”出現在執行語句中,或在給變數賦值時出現在等號右邊時,表示取物件的地址。

6.2.4指標的賦值 定義指標後必須先賦值,然後才能引用。

兩種賦值方法: (1)定義指標的同時進行初始化賦值: 儲存型別資料型別*指標名=初始地址; //定義指標的同時進行初始化賦值 (2)定義指標後,單獨使用賦值語句: 指標名=地址; //定義指標後,單獨使用的賦值語句 如果使用物件的地址作為指標的初值,或在賦值語句中將物件地址賦給指標變數,該物件必須在賦值之前就宣告過,而且這個物件的型別應該和指標型別一致。

多個指標可指向同一個變數。

一個陣列,可以用它的名稱來直接表示它的起始地址。

**陣列名實際上就是一個不能被賦值的指標,即指標常量。

**如: inta[10]; //定義int型陣列 int*ptr=a; //定義並初始化int指標 關於指標的型別,還應注意: (1)可以宣告指向常量的指標,此時所指物件的值不變,指標本身可變(可指向另外的物件): inta; constint*p1=&a; //p1是指向常量的指標 (2)可以宣告指標型別的常量(指標常量),這時指標本身(的值,為地址)不能被改變: int*constp2=&a; //p2是指標常量 (3)一般情況下,指標的值只能賦給相同型別的指標。

但void型別的指標,可以儲存任何型別的物件地址,任何型別的指標都可以賦值給void型別的指標變數。

經過使用型別顯示轉換,通過void型別的指標便可以訪問任何型別的資料。

void指標一般只在指標所指向的資料型別不確定時使用。

#include usingnamespacestd; intmain(){ //voidvoidObject; 錯誤,不能宣告void型別的變數 void*pv; //對,可以宣告void型別的指標 inti=5; pv=&i; //void型別指標指向整型變數 int*pint=static_cast(pv);//void型別指標賦值給int型別指標 cout< usingnamespacestd; intmain(){ intline1[]={1,0,0}; intline2[]={0,1,0}; intline3[]={0,0,1}; //定義整型指標陣列 int*pLine[3]={line1,line2,line3}; for(inti=0;i!=3;++i){ for(intj=0;j!=3;++j){ cout<成員名//通過物件指標訪問物件的成員 (*物件指標名).成員名 //上一行的等價形式 6.2.11.2this指標 this指標是一個隱含於每一個**類的非靜態成員函式(包括建構函式和解構函式)**中的特殊指標。

類的靜態成員函式中沒有this指標。

this指標用於指向正在被成員函式操作的物件。

this指標實際上是類成員函式的一個隱含引數。

在呼叫類的成員函式時,目的物件的地址會自動作為this指標的值,傳遞給被呼叫的成員函式,這樣被調函式就能夠通過this指標來訪問目的物件的資料成員。

this是一個指標常量,對於常成員函式,this同時又是一個指向常量的指標。

在成員函式中,可以使用*this來標識正在呼叫該函式的物件。

當區域性作用域中宣告瞭與類成員同名的識別符號時,對該識別符號的直接引用代表的是區域性作用域中所宣告的識別符號,這時,為了訪問該類的成員,可以通過this指標(比如this->x)。

6.2.11.3指向類的非靜態成員的指標 可將類的成員(變數、函式、物件等)的地址存放到一個指標變數中,這樣,可以通過這些指標直接訪問物件的成員。

宣告指向類的成員的指標: 型別說明符類名::*指標名; //宣告指向資料成員的指標 型別說明符(類名::*指標名)(參數列); //宣告指向函式成員的指標 對指向類的成員的指標賦值: 指標名=&類名::資料成員名; //對資料成員指標賦值 指標名=&類名::函式成員名; //對函式成員指標賦值 通過指向類的成員的指標(以及物件指標)訪問成員: 物件名.*類資料成員指標名 //訪問類的資料成員 物件指標名->*類資料成員指標名 //訪問類的資料成員 (物件名.*類成員函式指標名)(參數列) //訪問類的函式成員 (物件指標名->*類成員函式指標名)(參數列) //訪問類的函式成員 例子: intmain(){ Pointa(4,5); Point*p1=&a; //定義物件指標並初始化 //定義成員函式指標並初始化 int(Point::*funcPtr)()const=&Point::getX; //使用成員函式指標和物件名訪問成員函式 cout<*funcPtr)()<getX()<陣列物件名(陣列長度); //用vector定義動態陣列 尖括號中的型別名錶示陣列元素的型別。

陣列長度是一個表示式,表示式中可以包含變數。

比如: intx=10; vectorarr(x); //定義一個大小為10的int型動態陣列物件arr 用vector定義的陣列物件的所有元素都會被初始化。

另外,初值也可以自行指定,但只能為所有元素指定相同的初值: vector陣列物件名(陣列長度,元素初值); 訪問vector陣列物件的元素: 陣列物件名[下標表示式] //訪問vector陣列物件的元素 vector陣列物件的名字表示的就是一個陣列物件,而非陣列的首地址,因為陣列物件不是陣列,而是封裝了陣列的物件。

vector的詳細特性將在第10章介紹。

6.5深複製與淺複製 隱含的複製建構函式完成的只是淺複製。

預設的複製建構函式將兩個物件的對應資料簡單複製後,pointsArray1的成員points和pointsArray2的成員points具有相同的值,也就是說兩個指標指向的是同一記憶體地址,表面上好像完成了複製,但是並沒有形成真正的副本。

因此,當程式中移動pointsArray1中的點時,也影響到了pointsArray2。

這種效果就是“淺複製”。

淺複製還有更大的弊病,在程式結束之前pointsArray1和pointsArray2的解構函式會自動被呼叫,動態分配的記憶體空間會被釋放。

由於兩個物件共用了同一塊記憶體空間,因此兩次呼叫解構函式,將該記憶體空間釋放了兩次,於是導致執行錯誤。

解決“淺複製”問題的方法是編寫複製建構函式,實現“深複製”。

6.6字串 C語言中用字元型陣列來存放字串,C++中依然可以沿用這一方法。

不僅如此,C++標準庫還預定義了string類。

6.6.1用字元陣列儲存和處理字串 字串常量是用一對雙引號括起來的字元序列。

比如“abcd”,“Thisisastring.”。

在記憶體中按串中字元的排列次序順序存放,每個字元佔一個位元組,並在末尾新增空字元(nullcharacter)’\0’作為結尾標記。

這實際上是一個隱含建立的型別為char的陣列,一個字串常量就表示這樣一個陣列的首地址。

因此,可以把字串常量賦給字串指標,由於常量值是不能修改的,所以應將字串常量賦給指向常量的指標。

比如: constchar*STRING1="Thisisastring."; 這時,可以直接對STRING1進行輸出: cout<)。

string定義在名稱空間std中。

嚴格地說,string並非一個獨立的類,而是類别範本basic_string的一個特定化例項。

不過對使用者來說,其特點與一個類無異。

(注:以下內容大多參考《C++Primer(第5版)》及《物件導向程式設計——C++語言描述(原書第2版)》) 6.6.2.1初始化string物件 strings1; strings2(s1); //等價於strings2=s1; strings3("value"); //s3是字面值"value"的副本,不用加'\0' strings4(n,'c'); //用連續n個字元c組成的字串初始化s4 6.6.2.2讀取和輸出string物件 用操作符>>來輸入string型別字串會自動忽略開頭的空格,並從第一個真正的字元開始讀起,直到遇見下一處空格為止(不儲存任何空格)。

操作符<>s; //若輸入是“HelloWorld!” cout<>word) //反覆讀取,沒遇到檔案結束標記或非法輸入 cout<80) cout<s>t判斷s是否大於t>=s>=t判斷s是否大於或等於t[]s[i]訪問串中下標為i的字串 注意:這裡所說的對兩個字串的大小的比較,是根據字典順序的比較(大小寫敏感)。

假設有兩個字串s1與s2,二者大小的比較規則如下: (1)若兩個string長度不同,但較短的和較長的對應位上字元相同,則短



請為這篇文章評分?