陣列
將相同型態的變數集合而成並以一個共用的名稱來表示,稱之為陣列(array)。
利用索引(index)來處理陣列中元素(element)的存取動作。陣列中每個元素(element)所在記憶體位址
是連續的,不管是一維(one-dimension ;unidimension)陣列或是多維(multi-dimension)陣列,實際儲存
在電腦中的記憶體位址都是一維方式。稍後會介紹。
宣告一維陣列的通式(general form)
	T arrayName[size];	//T表示任何的資料型態,arrayName陣列名稱,size陣列大小
舉個例子:
	int arrInt[10];	//陣列名稱為arrInt,型態為int,大小為10
	int arrInt[0] = 10;	//設定第一個元素為10
	int arrInt[9] = 100;	//設定第十個元素為100
C/C++中,索引(index)是從 0 ~ size-1,本例即為arrInt[0]~arrInt[9]
第一個元素是arrInt[0]、第二個元素是arrInt[1]...10個元素是arrInt[9]。
而使用arrInt[10]會不會出問題呢?其實不會,因為C/C++沒有提供邊界檢查(bound checking)的功能
,也因為沒有邊界檢查,而造成更改到其它資料的問題。
接著來看陣列中示素記憶體分佈(layout)的位置:
	#include <iostream>

	int main()
	{
		int arrInt[10];
		int i;
		std::cout<<"size-of arrInt:"<<sizeof(arrInt)<<'\n';
		for( i = 0 ; i < 10 ; ++i )
			std::cout<<"address of arrInt["<<i<<"]:"<<&arrInt[i]<<'\n';
		return 0;
	}
	輸出:
	size-of arrInt:40
	address of arrInt[0]:0012FF58
	address of arrInt[1]:0012FF5C
	address of arrInt[2]:0012FF60
	address of arrInt[3]:0012FF64
	address of arrInt[4]:0012FF68
	address of arrInt[5]:0012FF6C
	address of arrInt[6]:0012FF70
	address of arrInt[7]:0012FF74
	address of arrInt[8]:0012FF78
	address of arrInt[9]:0012FF7C
陣列arrInt總共佔的記憶體大小為40,即陣列中元素的型態大小乘於個數 => sizeof(int) * 10
陣列arrInt的位址從0012FF58,即為arrInt[0]的位址,每加4,即為下一個元素的位址。
在C/C++arrInt&arrInt[0]得到的結果都是一樣的,但一般都寫成前者居多。

ps:以上陣列位址可能會與本人不一樣

一維陣列介紹完畢之後,接著以二維陣列來說明多維陣列的概念
宣告二維陣列的方式與一維陣列一樣,如下:
	T TwoDimArray[HEIGHT][WIDTH];
舉例:
	#include <iostream>

	int main()
	{
		int arrInt[4][3];
		int i,j;
	
		std::cout<<"size-of arrInt:"<<sizeof(arrInt)<<'\n';

		for( i = 0 ; i < 4 ; ++i )
		   for( j = 0 ; j < 3 ; ++j )
		      std::cout<<"address of arrInt["<<i<<"]["<<j<<"]:"<<&arrInt[i][j]<<'\n';
		return 0;
	}
	輸出:
	size-of arrInt:48
	address of arrInt[0][0]:0012FF50
	address of arrInt[0][1]:0012FF54
	address of arrInt[0][2]:0012FF58
	address of arrInt[1][0]:0012FF5C
	address of arrInt[1][1]:0012FF60
	address of arrInt[1][2]:0012FF64
	address of arrInt[2][0]:0012FF68
	address of arrInt[2][1]:0012FF6C
	address of arrInt[2][2]:0012FF70
	address of arrInt[3][0]:0012FF74
	address of arrInt[3][1]:0012FF78
	address of arrInt[3][2]:0012FF7C
在一般書中常喜歡以下列的表格方式來呈現二維陣列:
arrInt[0][0] arrInt[0][1] arrInt[0][2]
arrInt[1][0] arrInt[1][1] arrInt[1][2]
arrInt[2][0] arrInt[2][1] arrInt[2][2]
arrInt[3][0] arrInt[3][1] arrInt[3][2]
二維陣列的起始位址即為&arrInt[0][0],也等於arrInt,而arrInt[i][j]的位址怎麼計算呢?如下:
	address-of arrInt[i][j] = arrInt + ( i * WIDTH + j ) * sizeof(T)
以上arrInt陣列使用的WIDTH3Tint,所以可以自己推推看。
根據以上的計算可以知道,其實每個元素有不同的表示方式:
arrInt[1][0] 也等於 arrInt[0][3]
arrInt[1][1] 也等於 arrInt[0][4] 
arrInt[1][2] 也等於 arrInt[0][5]
arrInt[2][0] 也等於 arrInt[0][6] 	也等於arrInt[1][3]
arrInt[2][1] 也等於 arrInt[0][7] 	也等於arrInt[1][4]
arrInt[2][2] 也等於 arrInt[0][8]	也等於arrInt[1][5]
arrInt[3][0] 也等於 arrInt[0][9]  	也等於arrInt[1][6]   也等於arrInt[2][3]
arrInt[3][1] 也等於 arrInt[0][10] 	也等於arrInt[1][7]   也等於arrInt[2][4]
arrInt[3][2] 也等於 arrInt[0][11]	也等於arrInt[1][8]   也等於arrInt[2][5]
但在使用時一般還是使用深藍色字的部份。
arrInt佔記憶體空間大小為48,即為陣列中元素型態的大小乘於HEIGHT乘於WIDTH
本例中HEIGHT4WIDTH3,元素型態大小為sizeof(int)=4,所以得到4*3*4=48

而多維陣列其實與二維陣列是類同的,在此就不做介紹。
接下來介紹一般初學者學碰到的問題,如何將陣列當作引數傳給函式呢?
先看看一維,現在要撰寫一個函數,來印出陣列內的值:
	#include <iostream>
	void ShowBySizedArray(int arr[3],int size)	//Sized Array
	{
		int i;
		for( i = 0 ; i < size ; ++i )
			std::cout << arr[i] <<'\t';
		std::cout<< '\n';
	}
	void ShowByUnsizedArray(int arr[],int size)	//Unsized Array
	{
		int i;
		for( i = 0 ; i < size ; ++i )
			std::cout << arr[i] <<'\t';
		std::cout<< '\n';
	}
	void ShowByPointer(int *pArr,int size)		//Pointer
	{	
		int i;
		for( i = 0 ; i < size ; ++i )
			std::cout << pArr[i] <<'\t';
		std::cout<< '\n';
	}
	int main()
	{
		int arrInt[3] = { 2 , 3 , 4 };
	
		ShowBySizedArray(arrInt,3);
		ShowByUnsizedArray(arrInt,3);
		ShowByPointer(arrInt,3);
		return 0;
	}
顯示結果三個都一樣,有幾個問題值得思考:
(1)ShowBySizedArray中第一個參數int arr[3],在被呼叫時,真正產生大小為3的陣列嗎?
(2)ShowBySizedArray中第一個參數int arr[3],陣列大小3是否真正發生作用呢?
(3)ShowBySizedArray,ShowByUnsizedArrayShowByPointer三個函數操作情形一樣嗎?
口說無憑嘛!將以上程式的組語翻出來看看(未經優化後):
	25:       int arrInt[3] = { 2 , 3 , 4 };
	00401198   mov         dword ptr [ebp-0Ch],2
	0040119F   mov         dword ptr [ebp-8],3
	004011A6   mov         dword ptr [ebp-4],4
	26:
	27:       ShowBySizedArray(arrInt,3);
	004011AD   push        3
	004011AF   lea         eax,[ebp-0Ch]
	004011B2   push        eax
	004011B3   call        ShowBySizedArray (00401000)
	004011B8   add         esp,8
	28:       ShowByUnsizedArray(arrInt,3);
	004011BB   push        3
	004011BD   lea         ecx,[ebp-0Ch]
	004011C0   push        ecx
	004011C1   call        ShowByUnsizedArray (00401080)
	004011C6   add         esp,8
	29:       ShowByPointer(arrInt,3);
	004011C9   push        3
	004011CB   lea         edx,[ebp-0Ch]
	004011CE   push        edx
	004011CF   call        ShowByPointer (00401100)
	004011D4   add         esp,8
(若不清楚組語請自行參考書籍,若不清楚何謂stack frame的話,請參考作者所寫的【Stack Frame解析)
注意紅色的部份,ebp-0charrInt[0]的位址,即為陣列arrInt的位址
呼叫ShowBySizedArray,ShowByUnsizedArrayShowByPointer時,產生的assembly code傳遞引數都是一樣的
將傳進來的引數由右到左一一堆進(push)stack framepush 3即是將函數第二個引數推進stack framelea eax,[ebp-0ch]		lea ecx,[ebp-0ch]		lea edx,[ebp-0ch]
push eax 		or	push ecx		or	push edx
arrInt的位址推進(psuh)stack frame中,傳遞陣列的位址給函式

現在來回答以上問題的答案:
(1)不產生大小為3的陣列
(2)大小3並沒有產生作用,與int arr[]int *pArr是一樣的
(3)三個函式的操作方式都是一樣的,編譯器會把陣列的參數當作指標來處理
實際上並沒有傳遞整個陣列,而是以傳遞陣列的位址搭配其它的參數來處理。

一維陣列介紹完畢之後,接著來介紹二維陣列的傳遞:
	#include <iostream>
	void ShowByArray(int arr[][3],int height,int width)
	{
		int i,j;
		for ( i = 0 ; i < height ; ++i )
		{
			for ( j = 0 ; j < width ; ++j )
			{
				std::cout<<arr[i][j]<<'\t' ;
			}
			std::cout<<'\n';
		}
	}

	int main()
	{
		int arrInt[2][3] = { { 2 , 3 , 4 } , { 5 , 6 , 7 } };
		
		ShowByArray(arrInt,2,3);
		return 0;
	}
傳遞二維陣列時,第一個維度(dimension)或稱最左邊(leftmost)的維度可以忽略不寫,其餘必須明確表示出來。
為什麼呢?記得之前所說的二維陣列位址如何計算嗎?
	T TwoDimArray[HEIGHT][WIDTH];
	address-of TwoDimArray[i][j] = TwoDimArray + ( i * WIDTH + j ) * sizeof(T)
main中產生一個二維陣列arrInt時,在傳給函式時,編譯器需要讓函式知道這個陣列的維度(dimension)和它的
偏移量(offset),才能在函式中正確的計算出每個元素的位址,而進一步做存取的動作。如果不曉得什麼意思的話
,看個簡單的範例:
	#include <iostream>
	void ShowByArray(int arr[][3],int height,int width)
	{
		std::cout<<arr[1][2]<<std::endl;
	}

	int main()
	{
		int arrInt[2][3] = { { 2 , 3 , 4 } , { 5 , 6 , 7 } };
	
		ShowByArray(arrInt,2,3);
		return 0;
	}
assembly code:
	2:    void ShowByArray(int arr[][3],int height,int width)
	3:    {
	...
	4:        std::cout<<arr[1][2]<<std::endl;
	00401018   push        offset std::endl (00401af0)
	0040101D   mov         eax,dword ptr [ebp+8]	;ebp+8即為先前推入的arrInt位址,eax = arr
	00401020   mov         ecx,dword ptr [eax+14h]
	00401023   push        ecx
	00401024   mov         ecx,offset std::cout (00442308)
	00401029   call        std::basic_ostream<char,std::char_traits<char> >::operator<< (00401110)
	...
	5:    }
	...
	...
	9:        int arrInt[2][3] = { { 2 , 3 , 4 } , { 5 , 6 , 7 } };
	00401068   mov         dword ptr [ebp-18h],2
	0040106F   mov         dword ptr [ebp-14h],3
	00401076   mov         dword ptr [ebp-10h],4
	0040107D   mov         dword ptr [ebp-0Ch],5
	00401084   mov         dword ptr [ebp-8],6
	0040108B   mov         dword ptr [ebp-4],7
	10:
	11:       ShowByArray(arrInt,2,3);
	00401092   push        3
	00401094   push        2
	00401096   lea         eax,[ebp-18h]
	00401099   push        eax
	0040109A   call        ShowByArray (00401000)
	0040109F   add         esp,0Ch
紅色字14h,即為十進制的20,如何計算出來的呢?
函式得知arr是二維陣列,且偏移量(offset)3,算出arr[1][2] = arr + ( 1 * 3 + 2 ) * sizeof(int)
所以在傳遞二維陣列時,第一個維度可以忽略,而其它的維度必須明確的顯示。
傳遞多維陣列方式一樣,最左邊(leftmost)的維度可以忽略,其它的維度必須明確的顯示。
如果有興趣,可以參考以下的文章,加強對陣列的概念:
Arrays and pointers in C
Using Arrays

+另外有一個挺有趣的問題,請看以下文章
Writing Bug-Free C Code -> Chapter 2: Know Your Environment -> 2.1.1 The Array Operator
回目錄
Written By James On 2004/02/08 

Hosted by www.Geocities.ws

1