把《c++ primer》讀薄(4-1 c和c++數組)


督促讀書,總結精華,提煉筆記,拋磚引玉,有不合適的地方,歡迎留言指正。

c和c++的數組和指針都屬於低級的復合數據類型,比如c++的數組,類似vector容器,指針類似迭代器。低級的數據類型優勢是速度快。但是容易出錯,不好調試。現代c++程序,應該避免使用。

內置數據類型—數組,不方便存儲變長數據,定義之后長度固定(靜態數組),數組類似容器,比如,無法事先知道一個給定數組的長度,也沒有類似size函數來求數組長度,也沒有push_back()函數來添加元素,如果要更改數組長度,程序員只能重新建立數組,把舊的復制到新的。

記住現代c++程序,應該盡量是vector容器來替代數組,來存取同一類型的數據元素,只有當要求速度的前提下,如果使用容器不達標,那么再使用數組。

問題1、array由一系列的類型相同的元素構成,數組聲明包括數組元素個數和類型,數組最好定義之后就初始化。

數組的聲明

    //[]方括號表示聲明的是數組,里面的數字表明了數組包含的元素數目
    int states[50];//聲明50個整數的數組
    double code[365];//聲明365個浮點數的數組
    char chr[20];//聲明20個字符的數組

訪問數組內容可以用下標表示單個元素,下標數字也叫索引(index),從0開始計算。

數組的初始化

    //用花括號括起來,元素用逗號隔開
    int pow[8] = {1, 2, 3, 4, 5, 6, 7, 8};//只有標准c,c++支持此初始化語法
    //pow[0]首元素賦值為1,以此類推

注意:數組聲明的時候最好是初始化,雖然不會報錯,但是和普通變量一樣,使用沒有初始化的數組,里面元素的值不定,出現垃圾值。

數組初始化列表和大小不一致的情況

初始化列表里的元素個數和數組大小不一致,當初始化元素數目少於數組大小,多余的元素自動初始化為0。如果沒有初始化數組,那么和普通變量一樣,存取的內存原來就有的垃圾值。初始化了,只不過部分初始化,那么編譯器會自動初始化余下的元素為0。初始化了,但是初始化列表元素大於數組大小,那么編譯出報錯。

    //初始化沒有完全,編譯器自動后面給初始化為0
    double d[4] = {1};//不過vs2010里編譯運行,全都是0

    //error C2078: 初始值設定項太多
    double d[4] = {1, 2, 3, 4, 5, 6}; 

數組初始化小技巧

省略填寫數組大小,讓編譯器自動的去判斷數組的實際大小和初始化列表里的項目

    //空的方括號告訴編譯器去初始化列表里判斷數組實際大小
    char chr[] = {'a', 'b', ' ', '4'};
    //這里需要一個技巧,人工判斷數組大小容易出錯,使用sizeof運算符計算
    for (int i = 0; i < sizeof(chr) / sizeof(chr[0]); i++)
    {
        cout << "i + 1 = " << chr[i] << endl;
    }

注意技巧:sizeof(數組名)求的是數組總共多大(字節單位),sizeof(數組【0】)求的是數組中一個元素的大小(字節單位),兩者除法,就是數組的實際大小(元素個數)。

數組使用小技巧(常量大小)

    const int NUM = 4;//常量代表數組大小,推薦的技巧
    int days[NUM] = {1, 2, 3, 4};//良好的編程風格,如果以后想修改數組大小,只需要修改開頭的常量即可

    for (int index = 0; index < NUM; index++)
    {
        cout << days[index] << endl;
    }

對數組指定元素初始化

屬於C99的新特性,可以對數組指定的元素直接初始化,如果對數組最后一個元素初始化,那么傳統語法必須順次初始化到最后一個元素前,才能對最后一個元素初始化。

    //對最后一個元素初始化為1
    float f[3] = {0, 0, 1};

而c99規定可以直接去初始化

    //使用方括號【】,直接對數組某個元素初始化
    int i[3] = {i[2] = 100};//ok!需要加  數組名【】
    //int in[5] = {[4] = 10};//error C2059: 語法錯誤:“[”

對於一般的數組初始化,部分初始化之后,余下的自動初始化為0(這里vs2010編譯器不是這樣的,全部都是0 了),如果在指定的初始化元素后還有別的初始化值,那么這些數值自動對數組后續元素進行初始化,並且指定初始化元素的數值在第幾個元素處,那么也會同樣初始化這個元素。

比如in【0】=11在第5(0-5)個元素位置,那么同時打印第5個元素=11。說明初始化指定元素的同時,也用這個數值初始化本位置的元素了。

如果多次對數組某個元素初始化,則最后一次為准。

    int in[7] = {1, 2, in[2] = 10, 3, 4, in[0] = 11};   //首元素i0=11,i5=11
    int i;

    for (i = 0; i < 7; i++)
    {
        cout << "i" << i << " = " << in[i] << endl;
    }

只讀數組

如果只需要對數組讀取,而不進行修改,那么推薦關鍵字const

const int days[NUM] = {1, 2, 3, 4};//數組中每個元素都當作常量處理,和普通變量一樣,const數組也需要聲明的時候初始化

數組的賦值

使用數組下標(索引)為數組元素賦值,c和c++不支持整體賦值的初始化,或者就是單純的賦值,也不支持用花括號括起來的列表進行賦值(初始化除外)

    double d[SIZE] = {1, 2, 3};//ok這是可以的,列表進行初始化
    int i[SIZE];

    for (int count = 0; count < SIZE; count++)
    {
        i[count] = 2 * count;//ok,使用循環和數組下標給數組元素賦偶數值
    }

    //i = d;//error,c不支持數組整體賦值
    //i[SIZE] = d[SIZE];//error

    //d[SIZE] = {11, 22, 33};//不起作用,這種形式只有初始化可以使用,賦值必須使用索引

數組的邊界問題

千萬不要越界(類似數據類型的越界),數組索引不能越界,因為編譯器不會檢測這種錯誤。編譯器不會檢測索引的合法性,如果出現非法的索引,那么結果未知,有時候會中斷,但是也有時候可以執行,但是結果很奇怪(編譯器不同而不同),編譯器不檢測數組邊界,是出於信任程序員,可以節省時間和效率。 

注意:記住,數組計數原則上都從0開始,並且數組大小用符號常量代替。減少錯誤的發生。

再看數組大小的指定

前面可以用整型常量(unsigned)或者字符常量來指定大小,C99之前就是這兩種方法。

    const int const_m = 10;//在c語言(不同於c++)里,const值不被看作是常量,在c++里,這樣的定義的變量,也就是const常量,可以去做數組的維數。
    int n = 100;//定義了變量n

    double d1[10];// ok
    double d2[5 * 2 + 1];//ok
    double d3[];//error,沒有初始化,也沒有大小指定
    double d4[sizeof(int)];//ok,sizeof表達式在c里被認為返回一個整數常量
    double d5[-10];//error C2118: 負下標
    double d6[0];//error C2466: 不能分配常量大小為 0 的數組
    double d7[3.14];// error C2058: 常量表達式不是整型
    double d8[(int)3.14];//ok

    double d9[const_m];// error C2057: 應輸入常量表達式,“d9”未知的大小,不能分配常量大小為 0 的數組
    double d10[n];//error C2057: 應輸入常量表達式,“d9”未知的大小,不能分配常量大小為 0 的數組

c99之后,后兩種方式可以了,並且創建了一種新數組VLA(variable length array)變長數組,VLA。目的是為了讓c更適合做數值計算。

問題2、下列語法是不合法的

int ia[get_size()];

非法,get_size()是函數調用,不是常量表達式,不能用於定義數組的維數(維長度)。記住,必須是程序編譯期間就得確定數組的大小,運行期間的不行。

問題3、注意下列數組的初始值

//ia 為在函數體外定義的內置數組,也就是全局變量,各元素初始化為0
int ia[10];
//sa為元素類型為string 的數組,自動調用string 類的默認構造函數將各元素初始化為空字符串
string sa[10];

int main(void)
{
    //sa2 為元素類型為string 的數組,自動調用string 類的默認構造函數將各元素初始化為空字符串
    string sa2[10];

    //ia2 為在函數體內定義的內置數組,各元素未初始化,其值不確定
    int ia2[10];

    system("pause");
    return 0;
}

問題4、不要把數組和vector容器混淆

    //vector 對象不能用這種方式進行初始化。
    //vector<int> ivec = {0, 1, 1, 2, 3, 5, 8};
    int ia3[] = ivec;//錯誤。不能用vector 對象來初始化數組。

定義數組時可使用初始化列表來初始化數組的部分或全部元素。如果是初始化全部元素,可以省略定義數組時方括號中給出的數組維數值。

如果指定了數組維數,則初始化列表提供的元素個數不能超過維數值。如果數組維數大於列出的元素初值個數,則只初始化前面的數組元素,剩下的其他元素,若是內置類型則初始化為0,若是類類型則調用該類的默認構造函數進行初始化。

字符數組既可以用一組由花括號括起來、逗號隔開的字符字面值進行初始化,也可以用一個字符串字面值進行初始化。

問題5、列出使用數組而不是vector 的缺點

與vector 類型相比,數組具有如下缺點:數組的長度是固定的(C99之前),而且數組不提供獲取其容量大小的size 操作,也不提供自動添加元素的push_back 操作。因此,程序員無法在程序運行時知道一個給定數組的長度,而且如果需要更改數組的長度,程序員只能創建一個更大的新數組,然后把原數組的所有元素復制到新數組的存儲空間中去。與使用vector 類型的程序相比,使用內置數組的程序更容易出錯且難以調試。

問題6、注意數組下標越界問題

類似vector容器,string類型,別無他法,一定自習檢查。常見的“緩沖區溢出錯誤”,就是在編寫代碼的時候,引用了越界的下標,沒有檢查下標的范圍,導致出錯!

 

歡迎關注

 

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM