C語言中文網 目錄
首頁 > C語言專題 > 二維數組 閱讀:962

二維數組,C語言二維數組完全攻略

 數學中的行列矩陣,通常使用二維數組來描述,即用二維數組的第一維表示行,第二維表示列;生活中凡是能抽象為對象及對象的若干同類型屬性的問題,一般用二維數組來描述。

例如,若表示一個班級學生的語文、數學、外語、C 語言等 4 門課的成績數據。該問題可把每個學生看成一個對象,用二維數組的第一維來表示,如果有 50 個學生,則可設定二維數組第一維的大小為 50;成績可看成每個對象的屬性,且均可使用整型表示,可用二維數組的第二維來表示,每個對象(學生)含 4 個屬性(4 門課程),故第二維大小可設為 4。

再比如,某公司若統計某產品的某個月份的銷量數據,該問題可以把一周當成一個對象,一個月含 4 周,故 4 個對象,二維數組第一維可設為 4;日銷售量可看成每個對象的屬性,可用二維數組的第二維表示,對象(每周)含有 7 個屬性(7 天的日銷售量),故二維數組的第二維可設為 7。

二維數組的定義

同一維數組一樣,既支持 C89 標準的二維靜態數組,又支持 C99 標準的二維動態數組或變長數組。某些 C 編譯器還沒更新到支持 C99 標準的語法,故可能在一些編譯器中變長數組會報錯。如無特殊說明,教程中所指二維數組,均默認為靜態數組。

靜態二維數組定義的一般格式為:

類型 數組名[第一維大小][第二維大小];

其中,第一、二維的大小一般均為常量表達式。

例如:
int a[4][5];
定義了一個 4 行 5 列的 int 型二維數組 a。
float sc[3][4];
定義了一個 3 行 4 列的 float 型二維數組 sc。

如下二維數組的定義形式均是錯誤的。
int a[][3];//錯誤。編譯器無法確定所需空間
int a[2][];//錯誤。缺少列下標,編譯器無法確定所需空間
動態數組例子如下(僅做了解)。
int n=2;
int a[n][3];//動態數組,正確的C99語法。但在某些編譯器中可能報錯
int a[2][n];//動態數組,正確的C99語法
定義時未初始化的數組,其數據元素的值一般為無意義的隨機值,如:
int a[2][3];//該數組的6個元素均為隨機值
可以把二維數組看成一個特殊的一維數組,它的每個元素又是一個一維數組。例如,定義一個表示 3 個學生 4 門課程成績的二維數組:
int sc[3][4];
定義了一個 3 行 4 列的二維數組 sc,該二維數組可表示 3 個對象(學生),從這個角度看,該二維數組可以看成含 3 個對象(學生)的一維數組,3 個對象(元素)分別為:sc[0]、sc[1]、sc[2],其中 sc 為該一維數組名。

每個對象(元素)sc[i] 又是一個包含 4 個屬性(4 門成績)的一維數組,4 個屬性分別 為:sc[i][0](語文)、sc[i][1](數學)、sc[i][2](外語)、sc[i][3](C 語言)。每一行表示一個學生,每一列表示一門課程,形成如下所示的行列矩陣形式。

                           語文     數學     外語   c語言
sc[0](第一個學生)-----sc[0][0] sc[0][1] sc[0][2] sc[0][3]

sc[1](第二個學生)-----sc[1][0] sc[1][l] sc[1][2] sc[1][3]

sc[2](第三個學生)-----sc[2][0] sc[2][1] sc[2][2] sc[2][3]

二維數組名 sc 是首對象(第一個學生)sc[0] 的地址,即 sc=&sc[0]。二維數組名加 1 表示跳過一個對象,即 sc+1 為第二個對象(學生)sc[1] 的地址,sc+2 為第三個對象(學生)sc[2] 的地址。

從行列式角度分析,二維數組名即首行的地址,C 語言中的地址一般均是空間首地址。故二維數組名是首行首地址,該數組名加 1 表示跳過一整行,到達第二行的首地址,以此類推。

【例 1】以下二維數組 sc 用于保存 3 個學生的 4 門課程(語數外及 C 語言)成績。根據程序的運行結果,分析二維數組名的含義。
#include<stdio.h>
int main (void)
{
    int sc[3][4];
    printf("sc=%p\n",sc);
    printf ("&sc[0]=%p\n",&sc[0]);
    printf("sc+1=%p\n",sc+1);
    printf("sc+2=%p\n",sc+2);
    return 0;
}
程序某次運行的結果:
sc=0060FEE0
&sc[0]=0060FEE0
sc+1=0060FEF0
sc+2=0060FF00

程序分析:
1) 本例中定義的二維數組為 3 行 4 列,含 3 個學生對象,對應 3 行,每個學生對象包括 4 個屬性,對應 4 列。

2) 二維數組名 sc 相當于首對象即第一個學生 sc[0] 的地址,即 sc==&sc[0]。輸出地址一般使用格式控制符 %p 或 %x 或 %08x。由某次運行結果可知,sc 和 &sc[0] 的值均為十六進制數 0060FEE0。

由此可得:二維數組名為首對象或首行的地址。

3) 二維數組名加 1 表示跳過一個對象(一行)的空間,為下一個對象(下一行)的地址。即跳過一個對象所有屬性(一行中所有列元素)對應的空間,到達下一個對象(下一行)的起始位置。

而本例中一個對象有 4 個屬性,每個屬性均為整型變量,在 VC++6.0 中占 4 字節,故一個對象(一行)共占 4X4=16 個字節。故 sc、sc+1、sc+2 均相差 16 個字節,轉換為十六進制為 0x10。如運行結果所示。

4) 程序每次運行為某變量空間分配的起始地址可能有差別。

二維數組的引用

二維數組的引用格式為:

數組名[行下標][列下標];

注意引用數組元素時不能加類型,行下標、列下標均從 0 開始。且行下標和列下標的形式可以為常量、變量或表達式。

若 M 和 N 均為已定義的正整數:
int i,j; //i和j分別表示行下標和列下標.
int a[M][N]; //定義了一個M行N列的二維整型數組a
行下標i的范圍為 0?M-1。

列下標j的范圍為 0?N-1。

稱第幾個時,習慣上是從第 1 個開始,第 2 個,第 3 個,…,而不從第 0 個開始。但是二維數組的行下標及列下標均是從 0 開始的,為統一起見,本書中對數組元素的引用,采用行列序號來描述,如 a[i][j] 為 i 行 j 列元素,而不說成第 i 行第 j 列元素。例如:
int a[3][4];
a[0][0]; //為0行0列元素
a[2][1]; //為2行1列元素
a[1][1+2]; //為1行3列元素
例如:
int n=4, i, j ;
int a[3][4]; //定義了一個3行4列的二維數組a
對該數組的引用形式為 a[i][j],其中,行下標 i 的范圍為 0?2,列下標 j 的范圍為 0?3。以下對該二維數組的引用均是正確的。
a[2][n-1]=a[0][1+1] +a[2][2-1]; //a[0][2]、a[2][1]相加后賦給a[2][3]
a[3-1][n-1];//引用2行3列元素
以下對該二維數組的引用形式是錯誤的。
a[-2][1]; //行下標-2越界,應為0?2
a[3][2]; //行下標3越界,應為0?2
a[2][n]; //列下標n=4越界,應為0?3
a[2,3]; //引用形式錯誤,應為a[2][3]
a[2][]; //缺少列下標
int a[1][n-2]; //引用不能加類型,若為定義,第二維含有變量n,錯誤
【例 2】定義一個 2 行 3 列的二維數組,從鍵盤上輸入 6 個數值,依次給該二維數組的每個元素賦值,按每行三個元素輸出該二維數組及所有元素的和。

程序分析:
例題涉及對二維數組的賦值,一般使用雙重循環,外層循環控制行下標,內層循環控制列下標。二維數組的每個元素 a[i][j] 像普通變量一樣使用,使用 scanf 函數輸入時,一定要加 &。輸入時,可以輸入完 6 個數據后按一次回車鍵。

本題要求每輸出一行后換行,即輸出完一行中的所有列數據(內層循環結束)后換行。

實現代碼:
#include<stdio.h>
int main (void)
{
    int a[2][3]; //先定義,后賦值
    int i,j,s=0;
    printf("Input 6 integers:");
    for(i=0;i<2;i++)
    {
        for (j=0; j<3; j++)
        {
            scanf ("%d",&a[i][j]); //勿忘 &
            s+=a[i][j] ; //與上一條語句不能顛倒
        }
    }
    for(i=0;i<2;i++)
    {
        for (j=0; j<3; j++)
            printf("%d\t",a[i][j]); //使用\t 的作用
        printf ("\n"); //注意該輸出換行符的位置
    }
    printf("s=%d\n", s);
    return 0;
}
運行結果為:
Input 6 integers:1 2 3 4 5 6
1       2       3
4       5       6
s=21

二維數組的初始化

二維數組可以先定義,后賦值,在顯式賦值之前,二維數組的各數據元素是隨機值。也可以在定義二維數組的同時,采用初始化列表的形式對其元素賦初值,稱為二維數組的初始化。

二維數組的初始化方式通常有以下幾種。

1) 分行給出初始化數據,且每行的初始化數據個數等于列數。例如:
int a[2][3]={{1,2,3},{4,5,6}};
該初始化列表給出了兩行數據,每一行數據用一對大括號 {} 括起來,一行中的數據及行與行之間均用逗號隔開。這是一種較常用的二維數組的初始化方式。

該初始化語句相當于如下 6 條賦值語句。
a[0][0]=1; a[0][1]=2; a[0][2]=3;
a[1][0]=4; a[1][1]=5; a[1][2]=6;
由于初始化列表中明確給出了兩行數據,故定義該數組時,其第一維的大小可省略,編譯器能間接算出該數組的行數為 2,故依然可以確定其空間大小,因此,在對二維數組進行初始化時,其第一維的大小可以省略,即寫成如下形式:
int a[][3]={{l,2,3},{4,5,6}};
注意:第二維的大小一定不能省略!如下初始化均是錯誤的。
int a[2][] = {{l,2,3},{4,5,6}}; //錯誤。不能省略第二維大小
int a[][] = {{l,2,3}, {4,5,6}}; //錯誤。不能省略第二維大小
int a[][3]; //錯誤。沒有提供初始化列表時,兩維的大小都必須顯式給出
int a[2][3] = {{l,2,3},{4,5,6},{7,8,9}}; //錯誤。初始行數多于數組行數
如果把上面一條初始化語句改為省略第一維的大小,便是正確的,即:
int a[][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; //正確。間接得知該數組為三行
2) 分行給出初始化數據,但每行的初始化數據個數少于列數。例如:
int a[2][3]={{l,2},{4}};
該初始化列表給出了兩行數據,第一行給出兩個數據,少一個,對 int 型默認為補 0;第二行僅給出一個數據,少兩個,補兩個 0。所以上述初始化語句相當于:
int a[2][3]={{1,2,0},{4,0,0}};
同理:
int a[][3]={{0},{0}};
相當于:
int a[][3] = {{0,0,0},{0,0,0}}; //2行3列
3) 初始化數據沒有分行,容易產生混亂,不推薦這種方式。

如果初始化數據的個數是列數的整數倍,即:
int a[2][3]={l,2,3,4,5,6};
初始化數據以列數三個為一組,共分為兩組,且每組數據個數恰好等于列數 3,故第一組賦值給第 1 行,第二組賦值給第 2 行。初始化后,數組中各元素為:

1 2 3
4 5 6

例如:
int a[2][3]={1,2,3,4};
由于該二維數組列數為 3,初始化數據以三個為一組,共分為兩組,但第二組僅一個數據,少于列數 3,對 int 型數組用 0 補齊。故第一組數據 1,2,3 賦值給第一行,第二組補齊為三個數據 4,0,0 后,賦值給第二行。相當于 int a[][3]={1,2,3,4,0,0}; 初始化后, 數組中各元素為:

1 2 3
4 0 0

int a[][3] = {1,2,3,4,5,6,7,8}; //正確,可間接得知該數組為三行,不推薦
第三行不夠三個數據,用 0 補齊。故該語句相當于:
int 3[][3] = {1,2,3,4,5,6,7,8,0};
初始化后,數組中各元素為:

1 2 3
4 5 6
7 8 0

如果第一維大小沒有省略,則初始化數據的個數一定不能超過數組元素的總個數,否則報錯。例如:
int a[2][3] = {1,2,3,4,5,6,7,8};//錯誤。初始數據個數8多于數組總個數6

二維數組的存儲

二維數組在邏輯(表現形式)上可理解為矩陣形式(分行分列),但其物理存儲形式卻是連續的,即存完第一行,在其后面接著存儲第二行,第三行,…,如無特殊說明,本書中涉及對二維數組的表述一般指的是其邏輯形式即矩陣形式。

如 int a[3][4]; 其邏輯結構是 3 行 4 列的矩陣形式,如圖 1)  所示,而其存儲結構是連續的(線性的),如圖 2) 所示。

二維教組的應用舉例

【例 3】一個班級有 N 名學生,每個學生有 4 門課程(語文、數學、外語、C 語言),計算每個學生的平均分。編寫程序實現該功能需求。

問題分析:該問題可把每個學生當成一個對象,而每個對象(學生)有 5 個屬性:4 門課程成績及平均分。故該問題可使用二維數組來處理數據,該二維數組含有 N 個對象,故第一維的大小為 N,每個對象有 5 個屬性,故第二維的大小為 5。為便于驗證,本例中學生個數 N 設為 3 個。

實現代碼:
#include<stdio.h>
#define N 3
int main (void)
{
    float a[N][5],sum; //sum用來累加每個學生4門課的總成績
    int i, j ;
    printf ("輸入%d個學生信息(語、數、外、C語言成績):\n",N);
    for(i=0;i<N;i++)
    {
        sum=0.0; //對每個學生sum均初始為0
        printf ("NO%d:",i+1);
        for (j=0; j<4; j++) //每個學生僅輸人4門課成績,注意j<4
        {
            scanf("%f",&a[i][j]);
            sum+=a[i][j];
        }
        a[i][4]=sum/4; //每個學生的平均成績是計算出來的
    }
    printf ("\n學號\t語文\t數學\t外語\tC語言\t平均成績\n");
    for(i=0;i<N;i++)
    {
        printf("NO%d:\t", i+1);
        for(j=0;j<5;j++)
            printf ("%.1f\t",a[i][ j]) ; //保留一位小數,注意格式%.1f
        printf ("\n");
    }
    return 0;
}
運行結果為:
輸入3個學生信息(語、數、外、C語言成績):
NO1:82 91 88 93
NO2:83 84 80 91
NO3:73 79 86 81

學號    語文    數學    外語    C語言   平均成績
NO1:    82.0    91.0    88.0    93.0    88.5
NO2:    83.0    84.0    80.0    91.0    84.5
NO3:    73.0    79.0    86.0    81.0    79.8

精美而實用的網站,提供C語言、C++、STL、Linux、Shell、Java、Go語言等教程,以及socket、GCC、vi、Swing、設計模式、JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo