第六章 “另類”數組

 

動態數組與字符串常量可算是兩種“另類”數組。

        VLA可變長數組並不爲C89所支持,C99纔開始支持VLA。但如果想在只支持C89的編譯環境中使用VLA的話,怎麼辦呢?我們可以用動態數組來“模擬”,動態數組在矩陣的運算中很常見,常用來向函數傳遞一個大小可變的矩陣。動態數組的原理,是利用一塊或多塊動態分配的內存存儲各維的首地址,這樣就可以p[i][j]的形式訪問數組的數據了。但是,動態數組並非真正的數組,它只是對數組的一種模擬。由於具有數組類型的數組名是系統行爲,在用戶這一級沒法做到,因此只能以指針的形式存放首地址,sizeof(p)和sizeof(p[i])結果都是4字節。雖然動態數組是依靠動態分配內存來建立的,但動態的意義並非來自這裏,而是指大小可變。筆者覺得用“動態數組”這個名稱來命名非常適合,既不失大小可變的特徵,又可以跟VLA可變長數組區分開來。

        下面是建立動態數組的示例:

#include <stdio.h>
#include <stdlib.h>

void computedata(int *, int, int);

int main(void)
{
        int iData[100], x, y;
        do
        {
                printf("The product obtained by multiplying x and y must be less than 100!");
                printf("x=");
                scanf("%d", &x);
                printf("y=");
                scanf("%d", &y);
        }
        while(x*y > 100);
        computedata(iData, x, y);
        return 0;
}

void computedata(int *ipSource, int iRow, int iColumn)
{
        int **ipTemp, i, j;
        ipTemp = (int **)malloc(iRow*sizeof(int*));
        for(i=0; i<iRow; ++i) ipTemp[i] = ipSource+i*iColumn;
        for(i=0; i<iRow; ++i) for(j=0; j<iColumn; ++j) ipTemp[i][j] += 1;
        free(ipTemp);
        return;
}

以上示例把動態數組ipTemp的元素都加了1,由於只是示例,筆者省略了檢測數據合法性的代碼。iRow是第一維上界,iColumn是第二維上界,iData是源數據緩衝區,iRow*iColumn的積不能超過iData緩衝區的大小,否則就會越界了,但可以比它小。示例中iData被定義爲一維數組,當然根據自己的需要也可以用其它類型的緩衝區代替,例如動態分配的一塊內存,或者多維數組,如果是多維數組,例如三維數組int iData[10][10][10],調用computedata函數時,實參iData得做一些轉換:

computedata((int *)iData, x, y);或者computedata(&iData[0][0][0], x, y);都可以。

ipSource指針用來傳遞緩衝區的首地址,這個指針由於要用來計算各維的地址,因此最好定義爲一級指針,這樣比較方便。ipTemp是一個二級指針,這是因爲它指向的那塊內存存放的是指針,這些指針指向各維的首地址,對ipTemp的元素來說,ipTemp就是二級的。最後記得free(ipTemp);

        以上是定義一個二維動態數組的例子,多維動態數組的創建方法跟這個類似,下面給出三維動態數組的代碼:

void computedata(int *ipSource, int iHigh, int iRow, int iColumn)
{
        int ***ipTemp, i, j, k;
        ipTemp = (int ***)malloc(iHigh*sizeof(int**));
        for(i=0; i<iHigh; ++i) ipTemp[i] = (int **)malloc(iRow*sizeof(int*));
        for(i=0; i<iHigh; ++i) for(j=0; j<iRow; ++j) ipTemp[i][j] = ipSource+i*iRow*iColumn+j*iColumn;
        for(i=0; i<iHigh; ++i) for(j=0; j<iRow; ++j) for(k=0; k<iColumn; ++k) ipTemp[i][j][k] += 1;
        for(i=0; i<iHigh; ++i) free(ipTemp[i]);
        free(ipTemp);
        return;
}

        下面來討論字符串常量。
        衆所周知,C語言是沒有字符串變量的,因而,C89規定,字符串常量就是一個字符數組。因此,儘管字符串常量的外部表現形式跟數組完全不同,但它的確是一個真正的數組,實際上,字符串常量本身就是這個數組的首地址,並且具有數組類型,對一個字符串常量進行sizeof運算,例如sizeof("abcdefghi"),結果是10,而不是4。字符串常量與一般數組的主要區別,是字符串常量存放在靜態存儲區,而一般數組(非static)則是在棧中靜態分配的。由於字符串常量是數組首地址,因此可以數組引用的形式使用它,例如:

printf("%s", &"abcdefghi"[4]);

這將打印出字符串efghi。還可以這樣:

printf("%s", "abcdefghi"+4);

同樣打印出字符串efghi。實際上,&"abcdefghi"[4]等價於&*("abcdefghi"+4),去掉&*後,就是"abcdefghi"+4了。

我們可以利用字符串常量這些特性寫出一些有趣的程序來,例如:


#include <stdio.h>

int iLine=1;

int main(void)
{
 printf("%*s\n", 7-(iLine>4?iLine-4:4-iLine), "*******"+2*(iLine>4?iLine-4:4-iLine));
 if(++iLine != 8) main();
 return 0;
}

這個程序不使用任何數組形式的引用,不使用循環,就可以打印出用*號組合出來的菱形。當然,筆者並非鼓勵大家編寫這樣的代碼,但通過這樣的例子加深對字符串常量的認識,仍然是非常重要的。

發佈了23 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章