5.1數組的定義:
數組一旦被定義,它的維數和維界就不再改變。因此,除了結構的初始化和銷毀之外,數組只有存取元素和修改元素值的操作。
5.2數組的順序表示和實現下面我們先來看下數組順序存儲的表示:
代碼如下:
#include <stdarg.h> //提供宏va_start、va_arg、va_end //用于存取可變長參數表#define MAX_ARRAY_DIM 8//假設數組最大值為8typedef struct{ ElemType* base; //數組元素基址,由InitArray分配 int dim; //數組維數 int* bounds; //數組維界基址,由InitArray分配 int* constants; //數組映像函數常量基址,由InitArray分配}Array;我們先分析下:
這個va_start,va_arg,va_end我們遇到相關代碼再說。
這個*base是數組元素基址,所謂的元素基址,我們知道一維數組,二維數組,三維數組在邏輯上我們可以把他形象化,比如一維數組是線,二維是平面,三維是空間,但在內存中,他還是處于線性排列的,所以得有那么一個數組的元素基址,才能確定元素的位置。
這個dim,指的是數組的維度,比如剛剛所說的一維,二維,三維。
這個*bounds是存儲了每個維度所在的地址。
這個*constants這個是數組映像函數常量基址,可能有人會問,什么是映像函數。所謂的映像函數就是用以根據數組下標快速計算其存儲位置,有人說聽不懂!那舉個例子。假定每個元素之占用1個存儲單元.其實這就是:(b2*...*bn)*j1+(b3*...*bn)*j2+(b4*...*bn)*j3+...+[b(n-1)*bn]*j(n-2)+bn*j(n-1)+1*jn上面式子的變體.constants[0]*j1+constants[1]*j2+...+constants[n-2]*j(n-1)+constants[n-1]*jnbounds[]中存儲的每一維的多少
懂了吧。
下面是基本操作的函數原型說明:首先來看InitArray函數:
代碼如下:
Status InitArray(Array& A, int dim, ...){ //若維度dim和各維度長度合法,則構造相應的數組A,并返回OK if (dim<1 || dim>MAX_ARRAY_DIM) return ERROR; A.dim = dim; A.bounds = (int *)malloc(dim*sizeof(int)); if (!A.bounds) exit(OVERFLOW); //若各維長度合法,則存入A.bounds,并求出A的元素總數elemtotl int elemtotal = 1; // elemtotal是數組元素總數,初值為1(累乘器) va_list ap; va_start(ap, dim); //讀取可變參數的過程其實就是在堆棧中,使用指針,遍歷堆棧段中的參數列表,從低地址到高地址一個一個地把參數內容讀出來的過程 for (int i = 0; i < dim; i++) { A.bounds[i] = va_arg(ap, int); if (A.bounds[i] < 0) return UNDERFLOW; // 在math.h中定義為4 elemtotal *= A.bounds[i]; } va_end(ap); A.base = (ElemType*)malloc(elemtotal*sizeof(ElemType)); if (!A.base) exit(OVERFLOW); //求映像函數的常數Ci,并存入A.constant[i-1],i=1,...,dim A.constants = (int*)malloc(dim*sizeof(int)); if (!A.constants) exit(OVERFLOW); A.constants[dim - 1] = 1; //L=1,指針的增減以元素的大小為單位 for (int i = dim - 2; i >= 0; --i) A.constants[i] = A.bounds[i + 1] * A.constants[i + 1]; return Ok;}下面來分析下:這個MAX_ARRAY_DIM,是剛剛定義的宏,也就是最大只有8維。
這里的參數里面有"..."這個可能有人問。在此說明下。大家都懂PRintf這個也就是這樣,可變參數。那么如何讀取到底有多少個可變參數。那就是va_start,va_list,va_end,va_arg的事,下面來講解下va_start,va_list,va_end,va_arg。
va_list 是一個字符指針,可以理解為指向當前參數的一個指針,取參必須通過這個指針進行。<Step 1> 在調用參數表之前,定義一個 va_list 類型的變量,(假設va_list 類型變量被定義為ap);<Step 2> 然后應該對ap 進行初始化,讓它指向可變參數表里面的第一個參數,這是通過 va_start 來實現的,第一個參數是 ap 本身,第二個參數是在變參表前面緊挨著的一個變量,即“...”之前的那個參數;<Step 3> 然后是獲取參數,調用va_arg,它的第一個參數是ap,第二個參數是要獲取的參數的指定類型,然后返回這個指定類型的值,并且把 ap 的位置指向變參表的下一個變量位置;<Step 4> 獲取所有的參數之后,我們有必要將這個 ap 指針關掉,以免發生危險,方法是調用 va_end,他是輸入的參數 ap 置為 NULL,應該養成獲取完參數表之后關閉指針的習慣。說白了,就是讓我們的程序具有健壯性。通常va_start和va_end是成對出現。
這里面沒有用到va_arg,在下面的程序里面會用到。
函數都講解完了,下面講解下這程序的思路。
這個程序的思路是先把有多少維度的Array初始了,這個可變參數里面放的是根據dim的數來判斷的,比如dim為2,那么就是二維的,那么還要兩個可變參數,一個是第一維的元素個數,一個是第二維的元素個數。
這里elemotal*=A.bounds[i],就把維度里面的所有元素都讀取了出來
然后A.base就是第一個元素的地址。
開辟了A.base的空間后,現在弄映像函數。
下面是銷毀數組A代碼如下:
Status DestroyArray(Array& A){ //銷毀數組A if (!A.base) return ERROR; free(A.base); A.base=NULL: if (!A.bounds) return ERROR; free(A.bounds); A.bounds = NULL; if (!A.constants) return ERROR; free(A.constants); A.constants = NULL; return Ok;}分析:這個就是當那個指針地址不為空就free,然后把指針指向安全的地帶,也就是NULL。
下面是求元素在A中的相對位置。
代碼如下:
Status Locate(Array A, va_list ap, int& off){ //若ap指示的各下標值合法,則求出該元素在A中相對地址off int ind; off = 0; for (int i = 0; i < A.dim; i++) { ind = va_arg(ap, int); if (ind < 0 || ind >= A.bounds[i]) return OVERFLOW; off += A.constants[i] * ind; } return Ok;}分析:
這個函數是要結合下面這個函數看的。調用va_arg,它的第一個參數是ap,第二個參數是要獲取的參數的指定類型,然后返回這個指定類型的值,并且把 ap 的位置指向變參表的下一個變量位置。這個ap是下面這個Value函數的可變參數。
下面兩個函數代碼為:
Status Value(Array A, ElemType& e, ...){ //A是n維數組,e為元素變量,隨后是n個下標值。 //若各下標不超界,則e賦值為所指定的A的元素值,并返回OK。 va_list ap; int result; int off; va_start(ap, e); if ((result = Locate(A, ap, off)) <= 0) return result; e = *(A.base + off); return Ok;}Status Assign(Array& A, ElemType e, ...){ //A是n維數組,e為元素變量,隨后是n個下標值。 //若下標不越界,則將e的值賦值給所指定的A的元素,并返回OK va_list ap; va_start(ap, e); int off; int result; //這里的Statues被定義為int型 if (result = Locate(A, ap, off) <= 0) return result; *(A.base + off) = e; return Ok;}
|
新聞熱點
疑難解答