《c++ primer》第四章--陣列和指標
陣列是c++中類似於標準庫vector型別的內建資料結構,與vector相似,陣列也是一種儲存單一型別物件的容器,其中每個物件沒有單獨的名字,而是通過它在陣列中的位置對它進行訪問。
與vector型別相比,陣列的顯著缺陷在於:陣列的長度是固定的,而且程式設計師無法知道一個指定陣列的長度。陣列沒有獲得其容量大小的size操作,也不提供push_back操作在其中自動新增元素。如果需要更改陣列的長度,程式設計師只能建立一個更大的新陣列,然後把原陣列的所有元素複製到新陣列空間中去。~~~~(>_<)~~~~太費勁了...
在現在c++中,更多的使用vector容器來取代陣列,陣列被嚴格限制與程式內部使用,只有當效能測試表明使用vector無法達到必要的速度要求時,才使用陣列。(看來vector容器雖然用起來很方便,但是速度上不太行啊~~)。
&4.1.1
關於陣列的定義和初始化:
陣列的維數必須用大於等於一的常量表達式定義,此常量表達式只能包含整形字面值常量、列舉常量或者用常量表達式初始化的整型const物件。非const變數以及要到執行階段才知道其值的const變數都不能用於定義陣列的維數。 ————注意,是陣列的維數,不是陣列中某個 元素的值~~
例子:
// both buf_size and max_files are const
const unsigned buf_size = 512,max_files = 20 ;
int staff_size = 27; nonconst
const unsigned sz = get_size() ;//const value not known until run time
char input_buffer [ buf _ size ] ;//ok:const variable
string fileTable [max_size +1 ];// OK:const expression
double salaries [staff_size];//error: non const variable
int test_scores [ get_size ];//error: non const expression
int vals [ sz ];//error: size not known until run time
首先,雖然staff_size是用的字面值常量進行初始化,但是staff_size本身是一個——————非const物件,所以只有在執行時才能獲得它的值————————。
因此,使用該變數來定義陣列維數是非法的。而對於sz,儘管它是一個const物件,但是它的值要等到執行時呼叫get_size函式後才知道,因此,它也不能用於定義陣列維數。另一方面,由於 max_files 是const 變數,因此表示式
max_size+1
是常量表達式,編譯時即可計算出該表示式的值為21。
1.
顯示初始化陣列元素:
在定義陣列是,可為其元素提供一組用逗號分隔的初值,這些初值用 { } 括起來,稱為初始化列表:
const unsigned array_size=3;
int ia [ array _ size ] = { 0 , 1, 2 } ;
ps:
如果沒有顯示提供元素初值,則陣列元素會像普通變數一樣初始化:
※在函式體外定義的內建陣列,其元素均初始化為0;
※在函式體內定義的內建陣列,其元素無初始化;
※不管陣列在哪裡定義,如果其元素為類型別,則自動呼叫該類的預設建構函式進行初始化;如果該類沒有預設建構函式,則必須為該陣列的元素提供顯示初始化。
※※ 除非顯式地提供元素初值,否則內建型別的區域性陣列元素沒有初始化。此時,除了給元素賦值外,其他使用這些元素的操作無意義。 ※※
顯式初始化的陣列不需要提供指定陣列的維數,編譯器會根據列出的元素個數類確定陣列的長度。
EG:
int a [ ] = { 0 , 1 , 2 } ;
如果初始化列表中維數小於列出的元素個數,那麼會怎麼樣呢?
EG:
int ia [ 3 ] = { 0 , 1 , 2 , 3 , 4 , }; //error: 當家應該能猜到這個錯誤是什麼。。。初始值設定太多(⊙﹏⊙)b
※ 2
特殊的字元陣列:
字元陣列既可以用一組由花括號括起來、用逗號隔開的字元字面值進行初始化,也可以用一個字串字面值進行初始化。然而,要注意的是兩種初始化方式並不完全是相同,字串字面值包含一個額外的空字元(NULL) 用於結束字串。當使用字串字面值來建立新陣列時,將在新陣列中加入空字元。
char ca [ ] = { " C++" } ;
ca有四個元素,c,+,+,‘\0’
※ 注意:
與vector不同的是,一個數組不能用另一個數組進行初始化,也不能將一個數組賦值給另一個數組,這些操作都是違法的~~!違法!
※4.1.2
陣列的操作:
和vector操作一樣,陣列元素可以用下表操作符來訪問,陣列元素也是從0開始計數。
在這裡要注意一點,在c++中,vector使用vector::size_type作為下表型別,而陣列下標的正確型別則是size_t(也就是說,用int)也是可以的)~~~
i例如:
int main()
{
const size_t a_size = 5;
const int a_s = 6;
int ia[a_size];
int iaa[a_s];
}
這兩種方式都是可以的,但是在程式設計序時千萬別忘了int a_s前面的const,如果忘了就去上面看一下。
ps:
在我們使用陣列的時候,千萬要對陣列下標進行檢查,以防 “ 過界 ”,導致安全問題的最常見原因是所謂 “ 緩衝區溢位 ”,就是因為在程式設計時沒有檢查下標,這種錯誤常常會讓人忽略,但確實是不容輕視的一個錯誤。
※4.2
指標的引入:
首先,指標的概念很簡單:用於指向物件。和迭代器一樣,指標提供對其所指物件的間接訪問,只不過是指標結構更通用一些。當然,和迭代器不同的是,指標指向單個物件,而迭代器只能用於訪問容器內的元素。
*****************具體來說,指標儲存另一個物件的地址~
※4.2.2
指標的定義:
c++中使用*符號把一個識別符號宣告為指標:
vector < int > *pvec;
int *p1;
string *ps;
在c++中 也可以使用這種風格宣告指標:
string* ps;
ps也是指向string物件的一個指標。
PS:
不過在,上面的第二種宣告指標的風格很容易讓人誤解,而且在下面這個語句中:
string* ps1,ps2;
只有ps1是一個string * 型別,而ps2只是一個普通的string型別的物件~~!
4
指標可能的取值:
一個有效指標必然是一下三種狀態之一:
( 1 ): 儲存一個特定物件的地址;
( 2 ): 指向某個物件後面的另一個物件;
( 3 ) : 0值。
5:
我們應該儘量避免使用未初始化的指標,雖然指標可以不宣告時馬上定義,但是就像使用其他沒有初始化的變數一樣,在使用它的時候幾乎總會導致執行時崩潰。然而,導致崩潰的這一原因很難發現。
對大多數編譯器來說,如果使用未初始化的指標,會將指標中存放的不確定值視為地址,然後操縱該記憶體地址中存放的位內容。
6
指標初始化和賦值操作的約束:
對指標進行以上操作只能使用一下四種類型的值:
1:0值常量表達式
2:型別匹配的物件的地址。
3:另一個物件之後的下一個地址。
4:同類型領一個有效指標。
7
特殊型別的指標:void*指標
它可以儲存任何型別物件的地址~
當然,不允許使用void*型別的指標操縱它所指向的物件。
※4.2.3
指標操作
指標提供間接操縱其所指物件的功能,與對迭代器進行解引用一樣,對指標進行解引用可以訪問它所指的物件,* 操作符將獲取指標所指的物件。
string s ( " hello world " ) ;
string * SP = & s ;
cout << * sp;// prints hello world;
如果給你一個指標,要將指標所指的物件進行修改,可以怎樣做呢?
* SP = “ good bye ” ;
這樣指標所指的物件就改成了 “ good bye ” ,也沒啥難度是吧。。。
※ 注意給指標賦值或通過指標進行賦值:
對於初學的人來說,如何區分是給指標賦值還是通過指標進行賦值確實是挺困難的,不過我們可以記住一個重要的方法: 如果對左操作符進行了解引,那麼就是修改指標所指物件的值,如果沒有使用解引用,那麼就是修改指標本身。
考慮下面這個例子:
string s1 ( " some value " ) ;
string *sp1=&s1;
string s2 ( " another " ) ;
string *sp2=&s2;
*sp1=" a new value " ;
// 這裡修改了s1的值。
sp1= sp2;
// 這裡修改了sp1的指標,使其和sp2指向相同的物件。
※在這裡順便進行一下指標和引用的比較
雖然兩者都可以間接訪問另一個值,但是它們有兩個重要的區別:
1: 定義引用時沒有初始化是錯誤的;
2: 給引用賦值修改的是該引用所關聯的物件,因為它是物件的別名,。
4.3 c風格字串
2.2節中我們已經用過了字串字面值,並瞭解了字串的型別就是字串常量的陣列,現在可以更明確地認識到:它是一個 const char型別的陣列,c++從c語言繼承下來的一種通用結構是 c風格字串,而字串字面值就是這種型別的例項,然而,c風格字串既不能確切的歸結為c的型別,也不能歸結為c++,它是以空字元NULL結束的字元陣列。