天天看點

C語言虛拟僞數組

【8-6,8-7】如你所知,C編譯器為數組配置設定下标時總是從0開始,而且當程式使用下标通路數組元素時,它并不檢查下标的有效性,在這個項目中,你将要編寫一個函數,允許使用者通路“僞數組”,它的下标範圍可以任意指定,并伴以完整的錯誤檢查。下面是你将要編寫的這個函數的原型:

int array_offset(int arrayinfo[],...);
           

這個函數接受一些用于描述僞數組的維數資訊以及一組下标值,使用這個函數,使用者既可以以向量的形式配置設定記憶體空間,也可以使用mallco配置設定空間,但按照多元數組的形式通路這些空間,這個數組之是以被稱為“僞數組”是因為編譯器以為它是個向量,盡管這個函數允許它按照多元數組的形式進行通路。

這個函數的參數如下:

參數 含義
arrayinfo                        

  一個可變長度的整型數組,包含一些關于僞數組的資訊。

arrayinfo[0]指定僞數組具有的維數,它的值必須在1和10之間。

arrayinfo[1]和arrayinfo[2]給出第一維的下限和上限,

arrayinfo[3]和arrayinfo[4]給出第2維的下限和上限,

依次類推。

... 參數清單的可變部分可能包含多達10個的整數,用于辨別僞數組中某個特定位置的下标值。你必須使用va_參數通路它們,當函數被調用時,arrayinfo[0]參數将會被傳遞。

公式根據下面給出的下标值計算一個數組的位置。變量s1,s2等代表下标參數s1,s2等。變量lo1和hi1代表下标s1的下限和上限,它們來源于arrayinfo參數,其餘各維以此類推。變量loc表示僞數組的目标位置,它用一個距離僞數組起始位置的整型偏移量表示。

對于一維數組:

    loc = s1 - lo1

對于二維僞數組:

    loc = (s1 - lo1) × (hi2 - lo2 + 1) + s2 - lo2

對于三維僞數組:

    loc = [(s1 - lo1) × (hi2 - lo2 + 1) + s2 - lo2] × (hi3 - lo3 + 1) + s3 - lo3

對于四維僞數組:

    loc = [[(s1 - lo1) × (hi2 - lo2 + 1) + s2 - lo2] × (hi3 - lo3 + 1) + s3 - lo3] × (hi4 - lo4 + 1) + s4 - lo4

一直到第十維為止,都可以類似的使用這種方法推導出loc的值。你可以假定arrayinfo是個有效的指針,傳遞給array_offset的下标參數也是正确的。對于其他情況你必須進行錯誤檢查,可能出現的一些錯誤有:維的數目不處于1到10之間,下标小于low值,low值大于其對應的high值等,如果檢測到這些或者其他錯誤,函數應該傳回-1.

提示:把下标參數都複制到一個局部數組中,你接着便可以把計算過程以循環的形式編碼,對每一維都使用一次循環。

舉例:假定arrayinfo包含值2,4,6,1,5,-3,3.這些值提示我們所處理的是三維僞數組,第一個下标範圍從4到6,第二個下标範圍從1到5,第三個下标範圍從-3到3,在這個例子中,array_offset被調用時将有三個下标參數傳遞給它,下面顯示了幾組下标值以及它們所代表的偏移量:

C語言虛拟僞數組

這個題目有點意思,題目說明很長,實際上就是模拟一個多元數組,下标可以為負數,下标範圍有輸入參數決定:

難度系數5顆星,主要是設計的參數比較多,需要花時間了解題意,理清楚各個變量。

1、利用可變參數取出下标值:

va_list arg;
	va_start(arg, array_info);

    for (...){
    	index = va_arg(arg, int);
    }
           

2、目前次元的次元内偏移量:

目前次元的下标index -  目前次元的下限

3、整體偏移量:

為了友善了解,我們先舉一個例子:

假設數組array[3][4][5],我們知道這個三維數組實際上也是連續存儲的,如果需要計算array[2][3][4]這個數相對于數組起始位置的偏移,我們應該怎麼計算呢?

我們先看一下這個數組的結構,每個位置都用了位置坐标表示:

{

    {

        {000,001,002,003,004},

        {010,011,012,013,014},

        {020,021,022,023,024},

        {030,031,032,033,034}

    },

    {

        {100,101,102,103,104},

        {110,111,112,113,114},

        {120,121,122,123,124},

        {130,131,132,133,134}

    },

    {

        {200,201,202,203,204},

        {210,211,212,213,214},

        {220,221,222,223,224},

        {230,231,232,233,234}

    }

}

我們可以看到array[2][3][4]表示數組的最後一個元素,那怎麼計算出這個位置相對于數組起始位置的偏移呢?

2表示移動2行,每一行的大小就是 4*5 ,就是移動40個單元;

3表示移動3行,每一行的大小就是5,就是移動15個單元,就移動到了第二維的最後一行;

4表示移動4個單元,就到了最後一行的最後一個元素。

是以最終相對于起始位置的偏移就是40 + 15+ 4 = 59。

這個是數組本身的偏移計算方法,在這個僞數組這裡也是類似的:

目前次元的偏移量 = 上一個次元的偏移量 * 目前次元的大小 + 目前次元的偏移量

其中:目前次元的偏移量 = 目前次元的下标 - 目前次元下限

           目前次元的大小 = 目前次元上限 - 目前次元下限 + 1

将題目中給出的公式轉化為代碼:

#include <stdarg.h>
 
int array_offset(int array_info[], ...)
{
	int dimension = array_info[0];
	//超出次元範圍直接傳回-1
	if(1 > dimension || 10 < dimension){
		return -1;
	}
 
	
	int now;//目前次元
	int offset;//偏移量
	int index;//目前下标
 
	va_list arg;
	va_start(arg, array_info);
 
	//每次進兩位,有上下限兩個數
	for(now = 1, offset = 0; now <= dimension * 2; now += 2){
		//擷取一個下标值
		index = va_arg(arg, int);
		//和對應的array_info中上下限對比,超過傳回-1;
		if(index < array_info[now] || index > array_info[now + 1]){
			return -1;
		}
		//之前一次元的偏移量 乘以 次元 加上目前次元的偏移量
		offset = offset * (array_info[now + 1] - array_info[now] + 1) + index - array_info[now];
	}
 
	va_end(arg);
 
	return offset;
}

int main()
{
    int array_info[] = {3, 4, 6, 1, 5, -3, 3};
    printf("%d\n", array_offset(array_info, 4, 1, -3));
    printf("%d\n", array_offset(array_info, 4, 1, 3));
    printf("%d\n", array_offset(array_info, 5, 1, -3));
    printf("%d\n", array_offset(array_info, 4, 1, -2));
    printf("%d\n", array_offset(array_info, 4, 2, -3));
    printf("%d\n", array_offset(array_info, 6, 3, 1));
    return 0;
}
           

輸出:

0
6
35
1
7
88
           

上面都是數組下标的表示方法,這裡也可以改成指針表示:

#include <stdio.h>
#include <stdarg.h>

int array_offset(register int *arrayinfo, ...) {
    register int ndim;
    register int offset;
    register int hi, lo;
    register int i;
    int s[10];
    va_list subscripts;
    ndim = *arrayinfo++;//維數

    if (ndim >= 1 && ndim <= 10) {
        //取出坐标到s數組
        va_start(subscripts, arrayinfo);
        for(i = 0; i < ndim; i+=1) {
            s[i] = va_arg(subscripts, int);
        }
        va_end(subscripts);
        //計算每一維的偏移
        offset = 0;
        for(i = 0; ndim; ndim--,i++) {
            //擷取該次元的上下限
            lo = *arrayinfo++;
            hi = *arrayinfo++;

            //如果該次元坐标超出該次元上下限
            if (s[i] < lo || s[i] > hi) { return -1;}

            //
            offset *= (hi - lo  + 1 );//上一個offset * 該次元大小,就指向了該次元區間
            offset += s[i] - lo; //再加上在該次元内的偏移量
        }
        return offset;
    }
    return -1;
}

int main()
{

    int array_info[] = {3, 4, 6, 1, 5, -3, 3};
    printf("%d\n", array_offset(array_info, 4, 1, -3));
    printf("%d\n", array_offset(array_info, 4, 1, 3));
    printf("%d\n", array_offset(array_info, 5, 1, -3));
    printf("%d\n", array_offset(array_info, 4, 1, -2));
    printf("%d\n", array_offset(array_info, 4, 2, -3));
    printf("%d\n", array_offset(array_info, 6, 3, 1));

    return 0;
}
           

修改上面問題中的array_offset函數,使它通路以列主序存儲的僞數組,也就是最左邊的下标率先變化。這個新函數,array_offset2,在其它方面應該與原先那個函數一樣。

C語言虛拟僞數組

 舉例:假定arrayinfo包含值2,4,6,1,5,-3,3.這些值提示我們所處理的是三維僞數組,第一個下标範圍從4到6,第二個下标範圍從1到5,第三個下标範圍從-3到3,在這個例子中,array_offset被調用時将有三個下标參數傳遞給它,下面顯示了幾組下标值以及它們所代表的偏移量:

C語言虛拟僞數組

現在題目要求變了,以列主序存儲,就是最左邊的下标率先變化,其實就是把上面的下标清單倒着取,即可。

#include <stdio.h>
#include <stdarg.h>

int array_offset2(register int *arrayinfo, ...) {
    register int ndim;
    register int offset;
    register int hi, lo;
    register int *sp;
    int s[10];

    va_list subscripts;
    ndim = *arrayinfo++;//維數

    if (ndim >= 1 && ndim <= 10) {
        //取出坐标到s數組
        va_start(subscripts, arrayinfo);
        for(offset = 0; offset < ndim; offset+=1) {
            s[offset] = va_arg(subscripts, int);
        }
        va_end(subscripts);
        //從後往前計算每一維的偏移
        offset = 0;
        arrayinfo += (ndim * 2);//指向數組尾部
        sp = s + ndim;//指向最後一個下标
        while(ndim-- >=1 ) {
            hi = *--arrayinfo;
            lo = *--arrayinfo;

            if(*--sp > hi || *sp < lo) { return -1;}

            offset *= (hi-lo + 1);
            offset += (*sp -lo);
        }

        return offset;
    }
    return -1;
}

int main()
{
    int array_info[] = {3, 4, 6, 1, 5, -3, 3};
    printf("%d\n", array_offset2(array_info, 4, 1, -3));
    printf("%d\n", array_offset2(array_info, 4, 2, -3));
    printf("%d\n", array_offset2(array_info, 4, 1, -1));
    printf("%d\n", array_offset2(array_info, 5, 1, -3));
    printf("%d\n", array_offset2(array_info, 4, 3, -3));
    printf("%d\n", array_offset2(array_info, 5, 3, -1));
    printf("%d\n", array_offset2(array_info, 6, 1, -3));
    printf("%d\n", array_offset2(array_info, 4, 1, -2));
    printf("%d\n", array_offset2(array_info, 6, 5, 3));
    return 0;
}
           

這個題目還是有一定的難度,書上面是标注的5顆星難度,這要求熟練掌握C語言的數組和指針才能完成,值得好好研究了解。

繼續閱讀