2007年7月18日 星期三

C語言中陣列與指標的結合(二)

[轉錄自 http://blog.chinaunix.net/u1/36619/showart_335038.html]
C語言中陣列與指標的結合(二)

4.C語言中函數對陣列的傳遞
實際上,在C語言中沒有辦法把陣列本身傳遞給一個函數,
如果將陣列作為函數的實參,它將自動被改為指向陣列的指標。
C語言中函數參數的傳遞採用的是值傳遞的方式,除了陣列之外
的所以資料類型在傳遞時,都是使用複製一份完整的新拷貝的方式。

出於效率的考慮,C在傳遞陣列時,沒有將整個陣列複製,而是把陣列的位址壓入棧中傳遞給被調用函數,被調用函數從棧中獲取位址值,由此訪問被傳遞的陣列。因此,實際上被傳遞的參數是一個指向陣列的指標,而原本陣列大小的資訊則被丟失了。所以無論傳入的是陣列還是指標,被調用函數實際上得到的都只是指標。因此,C標準規定,在用作函數的形參時,以陣列形式定義的形參被編譯器當作指向該陣列的指標類型。

陣列參數自動改寫為指標的規則並不遞迴,二維陣列被改成的類型是指向陣列的指標,而不是指標的指標。所以,
char c[5][6];
對應的形參類型是
char (*p)[6];


而指標陣列
char *c[];
對應的形參類型才是
char **c;


5.向函數傳遞陣列
對於固定長度的陣列傳遞,情況比較簡單,如上面所說:
傳遞類型為
char c[5][6];
的陣列給函數,只需要把函數的形參類型聲明為
char (*p)[6];

char c[5][6];
即可。

但是要注意的是,被調用函數得到的只是指標,所以需要某種途徑把陣列的長度告訴被調用函數。一種方式是用某種特定的數值來表示陣列的結尾,比如對於字串,C語言定義了'\0',當檢索到結尾標誌時,函數就知道陣列結束了。另一種方式是,使用一個額外的參數來傳遞陣列長度,比如大家的老朋友,
int main(int argc, char *argv[]);

如果希望函數能接受的陣列參數更靈活一些,能具有可變長度,就需要比較曲折的方法了。因為陣列的第一維將被改為指標類型,陣列形參定義中最左邊的一維中的數字沒有效果。可以定義為,
char c[];
或者
char c2[][5];
這部分的解決了我們的問題,但如果希望c2的兩維都可變,就不能通過這種方式的參數傳遞了,需要使用的是指標陣列。


6.指標陣列
指標陣列的是以指標為元素的陣列。它的訪問方式和二維陣列很像,定義
char c[5][6];
char *cp[];
都可以用連續下標的方式訪問,
c[2][3] = cp[2][3];

但是它們實際上的訪問方式並不一樣:
先看c[2][3],它是取符號表中記錄的c位址,加上2*sizeof(char [6]),再加上3*sizeof(char),得到元素的位址。
而cp[2][3]則是取指針cp的值,加上2乘以指標寬度,得到指標cp[2]的地址;然後從位址取得內容,再以此內容為位址加上偏移量3*sizeof(char),得到元素的位址。

傳遞可變二維陣列的方法就是逐個把陣列第n行第一個元素的位址,賦予指標陣列的第n個元素,然後以指標陣列為參數,傳遞給被調用函數。

當然,也可以不把指標陣列作為二維陣列的載體,而把它作為多個不連續的不定長一維陣列的載體,不過記住,在傳遞時需要找到一種方法告知被調用函數所有的這些一維陣列的長度,對於字串這會比較容易。


7.使用動態陣列
在ANSI C中,不允許在運行時動態指定陣列的長度(C99中允許,參考C99 6.7.5)。為了突破這個限制,可以使用malloc()動態分配記憶體,(在cast為合適的類型後)以陣列下標的形式訪問這塊記憶體。更進一步,為了讓陣列的大小能真正的動態變化,可以根據陣列元素的實際數目,使用realloc()為你的陣列重新分配合適大小的記憶體。