數組初始化列表中的元素個數小於指定的數組長度時,不足的元素補以默認值。
原文:C/C++數組初始化的一些誤區
以前我這樣初始化一個數組,並自我感覺良好:
int a[5] = { 0 }; // 全部初始化為0
這種簡單的寫法讓我非常爽,於是我又想把數組全部初始化為1:
int a[5] = { 1 }; // 我想全部初始化為1
直到十分鍾前,我都以為這句代碼確實能夠將5個元素全部初始化為1,但事實跟我想的完全不同!(基礎的東西革命的本錢,疏漏不得啊)
全部初始化為0的那行代碼確實是沒問題的,可以正常工作。問題就出在想把數組全部初始化成一個非0的數,即非默認值,是行不通的(查看內存發現,只有數組的第一個元素被初始化為1,其他全為0)。這倒不是因為編譯器對初始化為0給了個后門,而是因為一條基本語法規則:
數組初始化列表中的元素個數小於指定的數組長度時,不足的元素補以默認值。
對於基本類型int來說,當然就是補int()即0了。再看一下非基本類型的數組:
string a[5] = { "foo" };
有了上面的規則,就很容易知道其實相當於:
string a[5] = { "foo", "", "", "", "" };
即后面4個元素調用了string的默認構造函數進行的初始化,而第一個則調用的string::string(const char*)進行的初始化。
還有一個區別:
int a[5]; string a[5];
如果不明確指出初始化列表,那么基本類型是不會被初始化的(除全局變量和靜態變量外),所有的內存都是“臟的”;而類類型則會為每個元素調用默認構造函數進行初始化。
注意,在C++11中中間的賦值號可以省略,即 int a[5]{1}; 並且,如果初始化列表為空,如 int a[5]{};,那將初始化所有元素為默認值,即與 int a[5]{0}; 等價
動態數組的初始化
說完了棧中的數組的初始化,我發現new一個數組和其又有一些不同:
int* a = new int[5]; string* a = new string[5]; int* a = new int[5] { 0 }; string* a = new string[5] { "foo" };
上面幾行代碼遵循棧中數組的初始化規則,除此之外這里還有一個新語法:
int* a = new int[5]();
看到這對圓括號,我想它該不會是元素的構造函數的參數列表吧,那么我可能會想將數組全部初始化為1:new int[5](1); 看起來很合理,但其實不行。事實上這對圓括號不是數組元素的構造參數,可能是整個數組的,它有三個重載版本:



看起來像是常引用、右值引用、和默認版本。所以假如已經有一個相同大小的數組b,試着用b來初始化a:
int* a = new int[5](b);
錯過了初始化時機(memset的誤區)
如果想在數組創建結束后再對其進行初始化,可以使用C函數memset(),但是memset的使用有個大問題,就是它只對char類型的數組管用:
char a[10]; memset(a, 1, 10); // 將每個元素設置為1
如果將上面的a數組換成int或其他類型的,就會出現問題,因為memset的內部實現是以字節為單位進行賦值的,int 類型大於一個字節(假設是4個),數組內存連續,如果有下面代碼:
int a[10]; memset(a, 1, sizeof(a));
將只會對前sizeof(a)即40個字節進行賦值1的操作,即給“前5個int”進行了賦值0x01010101的操作,失之毫厘謬以千里!
如果實在想再初始化,那么老老實實循環賦值吧。