1. 程式人生 > >C/C++陣列詳解(一維和二維)

C/C++陣列詳解(一維和二維)

陣列這東西,說說都懂,但是似乎並沒有完全吃透,導致很多地方有疑惑。所以再梳理一遍。

 

陣列定義

陣列是存放型別相同的物件的容器,這些物件本身沒有名字,需要通過其所在位置訪問。

從定義中可以看出,陣列存放的是物件且型別相同。所以不存在引用的物件(引用不是物件)

陣列的侷限性:陣列的大小確定不變,不能隨意向陣列中增加元素。如果不確定元素的確切個數,使用vector。

 

定義和初始化內建陣列

陣列的宣告形如a[d],其中a是陣列的名字,d是陣列的維度。維度說明陣列中的元素的個數,因此必須大於0。陣列中元素的個數也屬於陣列型別的一部分,編譯的時候維度應該也是已知的

。也就是說,維度必須是一個常量表達式:

unsigned int cnt = 42;   //不是常量表達式

int arr[10];     //含有10個整數的陣列  // 10是字面值常量

const int sz = 42;      //常量表達式 

constexpr int sz = 42; //C++11,常量

const int sz = get_size();  //不是常量表達式,sz的值只有在執行時才會獲取

int arr['a'];//‘a’是字元字面值常量,

 

預設情況下,陣列的元素被預設初始化

和內建型別的變數一樣,如果在函式內部定義了某種內建型別的陣列,那麼預設初始化會令陣列含有未定義的值。

定義陣列的時候必須指定陣列的型別,不允許使用auto關鍵字由初始化的列表推斷型別。

 

顯式初始化陣列

可以對陣列的元素進行列表初始化,此時可以忽略陣列的維度。如果在宣告時候沒有指定陣列的維度,編譯器可以根據初始值的數量計算並推測出來;相反,如果指明瞭維度,那麼初始值的總數量不應該超過指定的大小。如果維度比提供的初始值數量大,則用提供的初始值初始化靠前的元素,剩下的元素被初始化成預設值。(如int中的0,string中的“”)

constexpr int sz = 3;

int ia1[sz] = {0,1,2};

int a2[] = {0,1,2};

int a3[5] = {1,2,3};   //等價於a3[5]={1,2,3,0,0}

srting a4[3] = {"hi","bye"};  //等價於a4[3]={"hi","byr",""}

int a[2]  = {1,2,3};   //錯誤,初始值過多

 

字元陣列的特殊性

字元陣列有一種額外的初始化形式,我們可以用字串字面值對此類陣列進行初始化。當使用這種方式初始化時,一定要注意字串字面值的結尾處還有一個空字元,這個空字元也會像字串其他字元一樣被拷貝到字元陣列中去。

char a1[] = {'C', '+', '+'};   //列表初始化,沒有空字元

char a2[] = {'C', '+', '+', '\0'}; 

char a2[] = "C++";   //自動新增表示字串結束的空字元

const char a4[6] = "Daniel";   //錯誤:沒有空間可以存放空字元 

注意:有時候在Linux和Windows下不太一樣,Windows要求嚴格(出錯直接報錯,而Linux並不會,記憶體管理機制不一樣)

注意:特別是在拷貝的時候,注意結尾處有沒有加上'\0'

 

不允許拷貝和賦值

不能將陣列的內容拷貝給其他陣列作為初始值,也不能用陣列為其他陣列賦值:

int a[] = {0,1,2};

int a2[] = a;  //錯誤:不允許使用一個數組初始化另一個數組

a2 = a;    //錯誤:不能把一個數組直接賦值給另一個數組

 

理解複雜的陣列宣告

陣列能存放大多數型別的物件。例如,可以定義一個存放指標的陣列。又因為陣列本身就是物件,所以允許定義陣列的指標及陣列的引用。

 

①:int *ptrs[10];          //ptrs是含有10個整型指標的陣列

預設情況下,型別修飾符從右向左依次繫結,所以ptrs首先是一個大小為10的陣列,它的名字是ptrs,然後陣列中存放的是指向int的指標。

 

②:int &refs[10]  = /* ? */;     //錯誤:不錯在引用的陣列

因為陣列存放的是物件,而引用不是物件。

 

拓展:這裡還要注意一點:引用在定義的時候必須初始化

例1:int &ref; //錯誤

例2:class A{

public:

A::A();

~A::A();

int &ref;   //正確

};

解釋:引用在定義的時候必須初始化,而什麼叫定義,凡是有記憶體佔用行為的就是定義,否則就是宣告。

 

③:int (*Parray)[10] = &arr;    //Parray指向一個含有10個整數的陣列

其實這裡很好理解,就是按照運算子優先順序理解,首先()與[]優先順序一樣,所以按照結合,先執行()再[],()括號裡表明Parray是一個指標,然後在執行右邊的[],所以得知Parray是一個指向大小為10的陣列的指標,然後左邊的int表明陣列型別為int。

注意:等號右邊是對整個陣列取地址

 

知識點:陣列名在什麼時候退化為指標

以下情況均不能將陣列名s視為指標:

(1) sizeof(s)
(2) &s;
(3) 用陣列對陣列的引用進行初始化時

 

④:int (&arrRef)[10] = arr;      //arrRef引用一個含有10個整數的陣列

這個與③一樣。只不過arrRef是大小10,型別為int的陣列的引用。因為這裡是對陣列的引用,所以右邊arr理解為陣列而不是首元素地址。

 

 

 

多維陣列

嚴格來說,C++語言中沒有多維陣列,通常說的多維陣列其實是陣列的陣列。

int a[2][3];   //大小為2的陣列,每個元素是含有3個整數的陣列

int b[10][20][30] = {0};   //大小為10的陣列,每個元素是含有20個數組的陣列,然後這些陣列是含有30個整數的陣列

對於二維陣列來說,常把第一個維度稱作行,第二個維度稱作列。

 

多維陣列的初始化

int ia[3][4] = {                //三個元素,每個元素都是大小為4的陣列

        {0,1,2,3},              // 第一行的初始值

        {4,5,6,7},              // 第二行的初始值

        {8,9,10,11}           // 第三行的初始值

};

也可以是:

int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

也可以顯示初始化每行的首元素:

int ia[3][4] = {{ 0 },{ 4 },{ 8 }};  //其他未列出的元素執行預設值初始化,但是若省去花括號就不一樣了:

int ia[3][4] = {0,4,8};  //這裡顯示初始化第一行前3個元素,後面的元素預設初始化

 

 

理解如下宣告:

int ia[3][4] = { 0 };

int  (&row)[4]  = ia[1];    //把row繫結到ia的第二個4元素陣列上

解釋:首先二維陣列和一維陣列一樣,陣列名也是指向陣列首元素的指標。

但是:這裡一定要注意二維陣列的元素其實是陣列,

而上面說過陣列引用初始化時,陣列不褪化為指標,所以ia[1]代表第二個元素,也就是一個大小為4的陣列。