二、二維陣列
對於乙個n維陣列,其實質上還是乙個一維陣列,這個一維陣列的每個元素又都是乙個(n-1)維陣列。。以此類推。
複雜的不去深究,就看二維陣列a[m][n],實質是乙個由m個元素組成的一維陣列,每個元素又都是含n個元素的一維陣列,這個二維陣列共計m*n個元素。對於乙個二維陣列,它實質上是乙個一維陣列,但是是什麼樣的一維陣列?這個一維陣列的元素是什麼?
對於int a[2][3],邏輯上是2行×3列的整型矩陣,在記憶體分布中為線性儲存。其定義為:int a[2][3]=,};
容易看出這是含有兩個元素的一維陣列,每個元素又都是乙個含有3個整型資料的一維陣列。即a[0]=;a[1]=;
上述廢話那麼多,其實就是一句話:多維陣列實質上就是一維陣列,知道這一點對後續的理解非常重要。
1、二維陣列名
以上述int a[2][3]為例,定義上為二維陣列,而實質上就是一維陣列(其中a[0]=,a[1]=)。所以二維陣列的陣列名依然可以套用之前提到的一維陣列名的兩層含義:
第一層含義:表示的是整個陣列,依然是看似廢話可很有用的一句話。
(1)當a作為位址時(陣列名的第二層含義),表示的是等效一維陣列首元素的首位址,即a[0]的首位址;那麼(a+1)呢?因為a對應的是一維陣列元素,而這裡,這個一維陣列的元素又是乙個3個整型資料組成的一維陣列,所以a對應的單元大小為3*sizeof(int)=3*4=12個位元組,即(a+1)=x+12.
(2)對於&a,因為a的第一層含義(表示整個陣列),所以&a表示的是整個陣列的首位址,那麼(&a+1)呢?不再嘮叨,因為a對應整個陣列,所以單位大小為2*3*sizeof(int)=24byte。(&a+1)=x+24.
(3)&a[0][0]當然為二維陣列首元素的位址,(&a[0][0]+1)=x+1*sizeof(int)=x+4。
從上面描述,可以看出,雖然數值大小上a,&a,&a[0][0]是相等的,但含義不盡相同。下為示例驗證:
#includeintmain()
; printf(
"%p--%p--%p.\n
",a,&a,&a[0][0
]); printf(
"%p--%p--%p.\n
",a+1,&a+1,&a[0][0]+1
); getch();
return0;
}
2、二維陣列的等效一維陣列的陣列元素
對於上述a[2][3],其等效一維陣列元素分別為a[0]=;a[1]=;注意,這裡只是邏輯上的一種描述寫法,而非c**。
這裡陣列元素本身又為一維陣列,可以將a[0],a[1]分別看成這兩個陣列的陣列名。即a[0]是這個陣列的陣列名,a[1]是這個陣列的陣列名。
那麼依據前述陣列名含義的描述,因為a[0]是這個陣列的陣列名,我們初步判斷,在表示位址這個含義上:所以a[0]是這個陣列首元素的位址,即a[0]=&a[0][0],那麼(a[0]+1)=x+1*sizeof(int)=x+4;&a[0]為a[0]這個陣列的首位址,(&a[0]+1)=x+3*sizeof(int)=x+12。
同理(a[1]+1)=x+4;(&a[1]+1)=x+12。
通過下面這個示例也可以看出上面的初步判斷是正確的(雖然這僅是充分而非必要條件的證明=。=)
#includeintmain()
; printf(
"%p--%p--%p.\n
",a,&a,&a[0][0
]); printf(
"%p--%p--%p.\n
",a[0],a[0]+1,&a[0]+1
); getch();
return0;
}
3、指標在二維陣列中的應用
3.1一級指標訪問
對於int a[2][3];int *p;
有辦法可以通過指標變數p去訪問二維陣列a的元素麼?當然有,讓p指向二維陣列首位址就可以了。
那麼究竟通過哪種方法去設定呢?是p=a;還是p=&a,或p=&a[0][0]甚至p=a[0]還是p=&a[0]呢?
通過下例測試可以發現,不管哪種方法,都可以執行出正確結果;但其中只有當p=&a[0][0]和p=a[0]時候編譯不會提示warning資訊。
其他方法產生的warning資訊是因為p=x;這句左右值的位址型別不匹配而造成的,只是因為這是賦值語句,而且都是指標型別大小(4位元組),所以不影響賦值操作,也就可以執行。具體以乙個例子解釋警告資訊產生的原因:若用p=a;p是乙個指向整型資料的指標變數,而a作為位址時候表示的是等效一維陣列的首元素位址(該元素為包括3個整型資料的陣列型別);賦值左右指向型別不一致,造成warning。
#includeintmain()
;
int *p;
p=&a[0][0]; //
自己去改a,&a,&a[0],a[0],看編譯時候哪些會有warning資訊
printf("
%d--%p
",*p,p+1
); getch();
return0;
}
那麼我們就用p=&a[0][0](或p=a[0])去設定p的指向。設定完成後如何訪問呢?以訪問a[i][j]為例,兩種方法:一種對應於指標,以首位址為基準位址,加上偏移量,即(p+i*3+j);另外一種以下標方式p[i*3+j]這也不難理解。那麼可以用p[i][j]麼?我們理所當然認為可以。但自己測試一下吧,答案是不可以。
3.2陣列指標的訪問
對於二維陣列,之前說到其實質為乙個陣列元素為一維陣列的一維陣列;那麼如果有乙個指標,可以指向乙個陣列;再以該指標為基準進行偏移,不就形成乙個元素為一維陣列的一維陣列了麼?文字說明總是很拗口。看示例解釋:
假設pointer x指向乙個一維陣列,那麼(x+1)就是指向同等規模的下乙個一維陣列空間,(x+n)就是指向同等規模的第n個一維陣列空間。
那麼該如何表示乙個指向一維陣列的指標變數呢?變數定義時候格式都是如此:「型別說明符 變數名」,那麼以整形陣列指標為例就應該是:
(int [n]) (*p)。p表示乙個指向包含n個整形元素的陣列的指標變數。但據說因為這種形式看起來很不爽(其實我看著還是比較順眼的..),所以c語言裡把這個形式變換了一下,調整為int (*p)[n],這就是c語言裡對陣列指標的定義形式。
這個陣列的陣列名,陣列名的第一層含義是:表示這個陣列。那麼對a[0]取位址運算,即&a[0]表示的是這個陣列的首位址,對應單位為3*sizeof(int),和p對應的一致。
(5)是a麼?
是。a表示位址時,表示的是等效陣列的首元素的首位址,即a對應單位大小為sizeof(a[0]),即三個整形資料大小。與p所指一致。
上述(1)~(5)的分析可以通過如下示例觀察是否有warning資訊。
#includeintmain()
;
int (*p)[3
]; p=a; //
自行替換成其他形式,看看有無warning資訊
printf("
%p--%p
",&a[0][0
],p);
getch();
return0;
}
這裡重點說明一下:p,a都為位址,而且均為指向「包含3個整形資料的陣列」,那麼a作為位址,是個什麼型別的位址?是「指向包含三個整形資料陣列的位址」,所以a和p的指向型別一致,a可以當做「陣列指標型」位址。這一點的理解是很有必要的,對今後的位址運算,函式的二維陣列傳參都很有用。
那麼接著回到主題,「繫結」後如何通過p來訪問、操作乙個二維陣列呢?依然先對幾個形式做一下分析,先分析p,*p,**p。
以int a[2][3];int (*p)[3];p=a為例:
(2)*p是什麼?p是指向陣列的指標,那麼*p自然是乙個陣列咯,對於本例,*p就是a[0]這個陣列,這裡同時還明確:*p甚至可以看成這個陣列的陣列名使用,即具備陣列名的兩層含義:
第一層:*p表示a[0]這個陣列。說到這裡,感受是這真心不是一句廢話,因為*p表示的是陣列,那麼&(*p)表示是對整個陣列a[0]取首位址,則(&(*p)+1)表示的就應該是下乙個陣列首位址,即(&(*p)+1)=&a[1];而&(*p)不就是p嘛~看看上面的(1)中的結論:(p+1)=&a[1];=。=so這裡也互相驗證了一下)
那麼我要通過p訪問二維陣列中第i行第j列的元素應該如何表示?分三步:
對於二維陣列名和對應的陣列指標變數p,均可以通過下標和位址偏移的方式訪問元素。
上述3.1和3.2都描述了對二維陣列訪問的方法,比較明顯可以看出通過陣列指標方式更為方便:
1、定位(繫結)容易:直接p=a就可以。
2、繫結後操作方便,可以通過下標和基址偏移兩種方式進行。
C 陣列小結(1)
一 一維陣列 1 在記憶體空間上的存放 示例 對於int a 100 在記憶體中占用100 4 400位元組空間 對於char c 100 佔據記憶體空間為100位元組。2 陣列名 2.1陣列名是什麼?首先陣列名是陣列的名字,所以陣列名表示該陣列 這絕不是廢話,這句話非常有助於後面的理解。其次,陣列...
C 語法小結 2
7.虛函式在繼承層次的作用域 如果通過基類的引用或者指標呼叫虛函式成員,那麼編譯器將在基類中查詢函式名。假定找到了函式名,編譯器就檢查實參與形參是否匹配。這也是虛函式必須在基類和派生類中擁有同一原型的原因。如果在派生類中定義了與基類虛函式同名但函式原型不同的函式,那麼該函式將遮蔽基類定義的虛函式。總...
C語言陣列排序小結
讓我們先定義乙個整型陣列a n 下面用五種方法對其從小到大排序。1 冒泡法 冒泡法大家都較熟悉。其原理為從a 0 開始,依次將其和後面的元素比較,若a 0 a i 則交換它們,一直比較到a n 同理對a 1 a 2 a n 1 處理,即完成排序。下面列出其 void bubble int a,int...