在C語言中,數組是一個連續的線性存儲結構,數組名稱即爲數組首元素首地址,中括號被C語言解釋爲指向運算,通過對指針的加法,實現取得相應元素。對二維數組的使用則相當於定義一個二階指針,可以看做是一個一維數組裏每一個成員是一個一維數組,所以二維數組的名字就是一個指向指針的指針,即二階指針。通過兩次指向運算得到相應的元素。
但是在C語言中,對變量空間的申請必須要寫在一個函數的所有有效語句之前,使得無法得到用戶自定義的數組,如果想更改二維數組的行列值不得不更改源代碼,具體方式見示例1;
這種方式顯然不符合任意行列二維數組的要求,那麼我們想到,既然前面提到二維數組的本質是一個二階指針,那麼,我們是否可以通過用malloc()函數動態申請空間,然後把動態申請的空間的首地址賦值給一個一維數組的每一個元素。但是這樣做同樣存在問題,我們只能確定二維數組的”列“,並不能確定它的”行“。想要修改行值,依然需要更改源代碼。具體代碼見示例2;
所以,想要實現任意行列二維數組,就必須拋棄之前的二階指針的概念,因爲,在C語言中,能夠動態申請的空間只能通過malloc()或calloc()函數實現,而它們的返回值都只是(void *)類型的,爲一階指針。因爲就算C語言給出了能夠申請二維數組的函數,能夠返回二階指針,那如果我想生成一個三維數組怎麼辦呢,難不成還要給一個返回值爲三階指針的函數麼,那如果是n維數組呢......所以我們就必須考慮用一維數組來處理二維數組。其實這也是符合計算機的基本原理的,因爲在計算機中內存就是一個二維線性的存儲空間,我們看到的二維數組以及二階指針都是經過C語言處理過的”假象“!既然C語言能夠完成,我們自然也可以通過一個簡單的數學公式實現用一維數組實現任意行列二維數組。
數學公式基礎:1、一個矩陣的元素個數 = 行值 * 列值;
2、一個n行m列的矩陣的第i行第j列的元素,爲該矩陣的第(i - 1) * n + j個元素(i <= n, j <= m);
有了這個數學基礎,再多考慮C語言中數組下標從零開始這一特點,我們就可以輕鬆地編寫出能夠實現任意行列的二維數組的代碼!
由於二維數組不一定只在一個程序中使用。於是我們編寫一個.h文件,將初始化二維數組,以及對它的操作的函數寫進去,使得以後我們的程序需要用到二維數組的時候只需要在程序的前面加上#include“*.h”即可。
由於二維數組不一定存放int類型的數據或者某一種特定的數據,有可能是其他類型或者是用戶自己定義的結構體類型。因此,在該.h文件裏,給出了一個USER_TYPE類型,這就要求在引用該.h文件之前必須先做一個操作。即使用typedef讓USER_TYPE成爲自己想要的通過二維數組存儲的數據類型。
在之前所說的使用USER_TYPE存儲數據,那麼便出現了另一個問題,即,無法編寫輸出該數組的函數,我們不能通過一個簡單的printf("%d", USER_TYPE);來輸出這一類型,如果USER_TYPE是結構體類型,我們就無法輸出了。這裏我們通過指向函數的指針來解決,要求用戶必須編寫一個能夠輸出一個自己定義的USER_TYPE類型的數據的函數,在使用我們.h文件裏編寫的二維數組的輸出函數的時候,將自己編寫的函數的名稱作爲參數傳遞過去,而輸出二維數組的函數在編寫時只需要關注二維數組的整體性輸出,每一次的輸出,只需要通過形參裏的函數指針變量即可,至於該函數是如何輸出的,這是使用我們提供的.h文件的人所需要關心的。
本文章所給的主函數並不具有實際意義,只是對於dyadicArray.h內的函數的調用和測試。
具體代碼如下:
錯誤示例1:
#define MAX_ROW 3 #define MAX_COL 4 void main(void) { dyadicArray[MAX_ROW][MAX_COL]; }
錯誤示例2:
#include<malloc.h> #define MAX_ROW void main(void) { int *dyadicArray[MAX_ROW]; int maxCol; printf("請輸入您要得到的數組的列數:"); scanf("%d", &maxCol); for(i = 0; i < MAX_ROW; i++) { dyadicArray[i] = (int *)malloc(sizeof(int) * maxCol); } }
正確代碼: