陣列的定義,初始化和使用,C語言陣列詳解

2020-07-16 10:04:23
陣列可以說是目前為止講到的第一個真正意義上儲存資料的結構。雖然前面學習的變數也能儲存資料,但變數所能儲存的資料很有限。不僅如此,陣列和指標(後續會講)是相輔相成的,學習陣列可以為學習指標打下基礎。

那麼到底什麼是陣列呢?顧名思義陣列就是很多數的組合!那麼這些數有沒有什麼要求呢,是不是不管什麼陣列合在一起都是陣列呢?同一陣列中儲存的數必須滿足以下兩個條件:
  1. 這些數的型別必須相同。
  2. 這些數在記憶體中必須是連續儲存的。

換句話說,陣列是在記憶體中連續儲存的具有相同型別的一組資料的集合。

一維陣列

一維陣列的定義方式如下:

型別說明符 陣列名[常數表示式];

例如:
int a[5];
它表示定義了一個整型陣列,陣列名為 a,定義的陣列稱為陣列 a。陣列名 a 除了表示該陣列之外,還表示該陣列的首地址(關於地址現在先不討論,稍後講指標的時候再說)。

此時陣列 a 中有 5 個元素,每個元素都是 int 型變數,而且它們在記憶體中的地址是連續分配的。也就是說,int 型變數占 4 位元組的記憶體空間,那麼 5 個int型變數就佔 20 位元組的記憶體空間,而且它們的地址是連續分配的。

這裡的元素就是變數的意思,陣列中習慣上稱為元素

在定義陣列時,需要指定陣列中元素的個數。方括號中的常數表示式就是用來指定元素的個數。陣列中元素的個數又稱陣列的長度

陣列中既然有多個元素,那麼如何區分這些元素呢?方法是通過給每個元素進行編號。陣列元素的編號又叫下標

陣列中的下標是從 0 開始的(而不是 1)。那麼,如何通過下標表示每個陣列元素的呢?通過“陣列名[下標]”的方式。例如“int a[5];”表示定義了有 5 個元素的陣列 a,這 5 個元素分別為 a[0]、a[1]、a[2]、a[3]、a[4]。其中 a[0]、a[1]、a[2]、a[3]、a[4] 分別表示這 5 個元素的變數名。

為什麼下標是從 0 開始而不是從 1 開始呢?試想,如果從 1 開始,那麼陣列的第 5 個元素就是 a[5],而定義陣列時是 int a[5],兩個都是 a[5] 就容易產生混淆。而下標從 0 開始就不存在這個問題了!所以定義一個陣列 a[n],那麼這個陣列中元素最大的下標是 n–1;而元素 a[i] 表示陣列 a 中第 i+1 個元素。

另外,方括號中的常數表示式可以是“數位常數表示式”,也可以是“符號常數表示式”。但不管是什麼表示式,必須是常數,絕對不能是變數。通常情況下 C 語言不允許對陣列的長度進行動態定義,換句話說,陣列的大小不依賴程式執行過程中變數的值。非通常的情況為動態記憶體分配,此種情況下陣列的長度就可以動態定義,這個稍後會講。

一維陣列初始化

一維陣列的初始化可以使用以下方法實現:
1) 定義陣列時給所有元素賦初值,這叫“完全初始化”。例如:
int a[5] = {1, 2, 3, 4, 5};
通過將陣列元素的初值依次放在一對花括號中,如此初始化之後,a[0]=1;a[1]=2;a[2]=3;a[3]=4;a[4]=5,即從左到右依次賦給每個元素。需要注意的是,初始化時各元素間是用逗號隔開的,不是用分號。

2) 可以只給一部分元素賦值,這叫“不完全初始化”。例如:
int a[5] = {1, 2};
定義的陣列 a 有 5 個元素,但花括號內只提供兩個初值,這表示只給前面兩個元素 a[0]、a[1] 初始化,而後面三個元素都沒有被初始化。不完全初始化時,沒有被初始化的元素自動為 0。

需要注意的是,“不完全初始化”和“完全不初始化”不一樣。如果“完全不初始化”,即只定義“int a[5];”而不初始化,那麼各個元素的值就不是0了,所有元素都是垃圾值。

你也不能寫成“int a[5]={};”。如果大括號中什麼都不寫,那就是極其嚴重的語法錯誤。大括號中最少要寫一個數。比如“int a[5]={0};”,這時就是給陣列“清零”,此時陣列中每個元素都是零。此外,如果定義的陣列的長度比花括號中所提供的初值的個數少,也是語法錯誤,如“a[2]={1,2,3,4,5};”。

3) 如果定義陣列時就給陣列中所有元素賦初值,那麼就可以不指定陣列的長度,因為此時元素的個數已經確定了。程式設計時我們經常都會使用這種寫法,因為方便,既不會出問題,也不用自己計算有幾個元素,系統會自動分配空間。例如:
int a[5] = {1, 2, 3, 4, 5};
可以寫成:
int a[] = {1, 2, 3, 4, 5};
第二種寫法的花括號中有 5 個數,所以系統會自動定義陣列 a 的長度為 5。但是要注意,只有在定義陣列時就初始化才可以這樣寫。如果定義陣列時不初始化,那麼省略陣列長度就是語法錯誤。比如:
int a[];
那麼編譯時就會提示錯誤,編譯器會提示你沒有指定陣列的長度。

下面給大家寫一個簡單的程式:
# include <stdio.h>
int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    int i;
    for (i=0; i<5; ++i)
    {
        printf("%dn", a[i]);
    }
    return 0;
}
輸出結果是:
1
2
3
4
5

a 表示陣列的名字,[5] 表示這個陣列有 5 個元素,並分別用 a[0]、a[1]、a[2]、a[3]、a[4] 表示。並分別把花括號內的 1、2、3、4、5 賦給變數 a[0]、a[1]、a[2]、a[3]、a[4]。再次強調,下標從 0 開始,即從 a[0] 開始,而不是 a[1]。

也可以用 scanf 手動從鍵盤對陣列進行初始化:
# include <stdio.h>
int main(void)
{
    int a[5] = {0};  //陣列清零初始化
    int i;
    printf("請輸入5個數:");
    for (i=0; i<5; ++i)
    {
        scanf("%d", &a[i] );
    }
    for (i=0; i<5; ++i)
    {
        printf("%dx20", a[i]);
    }
    printf("n");
    return 0;
}
輸出結果是:
請輸入5個數:1 2 3 4 5
1 2 3 4 5

同使用 scanf 給字元陣列輸入字串時有所不同,輸入數位時必須用 for 迴圈進行輸入。而輸入字串時無須用迴圈,直接用 scanf 就可以了。

一維陣列元素的參照

陣列必須先定義,然後使用。C 語言規定,只能逐個參照陣列元素,而不能一次參照整個陣列。前面講過,陣列元素的表示形式為:

陣列名[下標]

下標可以是整型常數或整型表示式,比如:
a[0] = a[5] + a[7] - a[2 * 3]
千萬要注意,定義陣列時用到的“陣列名[常數表示式]”和參照陣列元素時用到的“陣列名[下標]”是有區別的,定義陣列時的常數表示式表示的是陣列的長度,而參照陣列元素時的下標表示的是元素的編號。比如:
# include <stdio.h>
int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};  //定義長度為5的陣列a
    int t;
    t = a[3];  /*參照陣列a中下標為3的元素a[3], 此時的3不代表陣列的長度*/
    printf("t = %dn", t);
    return 0;
}   
輸出結果是:
t = 4

“int a[5];”是定義了有 5 個元素的陣列,這 5 個元素分別為 a[0]、a[1]、a[2]、a[3]、a[4]。而 t=a[3] 中的 a[3] 不是陣列,只是其中的元素 a[3]。

因此,下面這個程式是錯的:
# include <stdio.h>
int main(void)
{
    int a[5];
    a[5] = {1, 2, 3, 4, 5};
    return 0;
}
錯誤的原因是下面的 a[5] 不是陣列。只有在定義的時候“a[常數]”表示的才是陣列,此時方括號中的數位才表示陣列長度。除此之外程式中任何地方看到“a[常數]”都不是陣列,都只是陣列的一個元素、一個變數,此時的“常數”表示的是元素的下標。

此外,當給元素單獨賦值時不能加大括號,因為元素就是變數,即 a[5] 只是一個變數名。前面是怎麼給變數賦值的現在就怎麼給陣列元素賦值,比如“a[5]=1;”。但是對於上面這個程式,這麼寫還是錯誤的。因為陣列元素的下標是從 0 開始的,陣列 a 的元素只有 a[0]~a[4],並沒有 a[5] 這個元素。