数组的实现——数据结构
由于存储单元是一维的结构,而数组是个多维的结构,则用一组连续的存储单元存放数据元素就有次序约定的问题了。
假设现在有一个三维数组A[5][6][7],现在初始化其数据结构时,不难联想到,首先要说明存放的数据类型(也可以是数组元素的基址,但元素的类型是一定要说明的),其次,就是数组的维数,还有就是每一维的大小。现在继续考虑,假设现在这个数组已经被我们定义成了这个结构,当我们想要取出元素A[3][2][1],由于存储单元是连续的顺序存储结构,必须要算出其相对于A[0][0][0]的地址,不难算出其结果:A[3][2][1]=3*(6*7)+2*7+2(其各维的索引下标都是从0开始的)。我们可以总结对任意的n维数组,设每一维的大小为bi,其元素的存储位置如下:
Locate(j1,j2,....,jn)=Locate(0,0,....,0)+[(j1*bn*bn-1*....*b2)+(j2*bn*bn-1*....*b3)+....+(jn-1*bn)+(jn)]
于是为了对数组元素定位的方便,在其数据结构中添加一项数组映像函数,其实质就是计算每一个单独维其向下所含有的元素的个数(bn*bn-1*...*bi+1 :如本例中的A,其映像函数的值分别为7*6和7)。
综上其结构如下:
typedef struct { ElemType *base; //数组元素的基址, int dim; //数组的维度 int *bound; //数组维界的基址 int *constants; //数组映像函数常量的基址 }Array;
在开始数组的初始化工作之前,由于不知道输入的维数,以致在写初始化函数的时候,含有不确定个数的参数,所以有必要来了解一下va_list(以下来自百度百科)
VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件:#include <stdarg.h>,用于获取不确定个数的参数
#include<stdarg.h> #include<stdio.h> void vltest(int i, float k, ...) //...代表可变参数 { va_list vl; //定义va_list变量vl,该变量是指向参数的指针 va_start(vl, k); // 参数一:va_list变量vl;参数二:va_list变量vl中最后一个固定参数 int j = va_arg(vl, int); // 参数一:va_list变量vl;参数二:可变参数的类型,返回值j即可变参数 double m = va_arg(vl, double); // 同上 unsigned long n = va_arg(vl, unsigned long); // 同上 va_end(vl); // 结束可变参数的获取 printf("i = %d; k = %.1f; j = %d; m = %lf; n = %lu\r\n", i,k, j, m, n); } void main() { int i=1,j=8; double m=3; float k=2.0; unsigned n=0; vltest(i,k,j,m,n); }
结果如图:
//数组的初始化 int Init_Array(Array &A,int dim,...) { //若输入的数组维度dim和各维的长度合法,则初始化数组,返回1 if(dim<1 || dim>MAX_ARRAY_DIM) return 0; A.dim=dim; A.bound=(int *)malloc(dim * sizeof(int)); if(!A.bound) exit(-2); //overflow //各维的长度合法,则存入A.bound中,并求出A的元素总数 int elemTotal=1; va_list ap; va_start(ap,dim); //存放变长参数表信息的数组 int i; for(i=0;i<dim;i++) { A.bound[i]=va_arg(ap,int); if(A.bound[i]<0) return -1; elemTotal*=A.bound[i]; } va_end(ap); A.base=(ElemType *)malloc(elemTotal*sizeof(ElemType)); if(!A.base) return -1; //求映像函数的常数,同时存入A.constants中去 A.constants=(int *)malloc(dim*sizeof(int)); if(!A.constants) return -1; A.constants[dim-1]=1; for(i=dim-2;i>=0;i--) A.constants[i]=A.constants[i+1]*A.bound[i+1]; return 1; }
数组的初始化完成之后,其他的工作就很简单了。
数组的赋值与取值案例如下:
#include<stdio.h> #include<stdlib.h> #include<stdarg.h> //标准头文件,提供宏va_start、va_arg和va_end,用于存储变长的参数表 #define ElemType int #define MAX_ARRAY_DIM 8 //定义数组的最大维数为8 typedef struct { ElemType *base; //数组元素的基址,由Init_Array函数分配 int dim; //数组的维度 int *bound; //数组维界的基址 int *constants; //数组映像函数常量的基址 }Array; //数组的初始化 int Init_Array(Array &A,int dim,...) { //若输入的数组维度dim和各维的长度合法,则初始化数组,返回1 if(dim<1 || dim>MAX_ARRAY_DIM) return 0; A.dim=dim; A.bound=(int *)malloc(dim * sizeof(int)); if(!A.bound) exit(-2); //overflow //各维的长度合法,则存入A.bound中,并求出A的元素总数 int elemTotal=1; va_list ap; va_start(ap,dim); //存放变长参数表信息的数组 int i; for(i=0;i<dim;i++) { A.bound[i]=va_arg(ap,int); if(A.bound[i]<0) return -1; elemTotal*=A.bound[i]; } va_end(ap); A.base=(ElemType *)malloc(elemTotal*sizeof(ElemType)); if(!A.base) return -1; //求映像函数的常数,同时存入A.constants中去 A.constants=(int *)malloc(dim*sizeof(int)); if(!A.constants) return -1; A.constants[dim-1]=1; for(i=dim-2;i>=0;i--) A.constants[i]=A.constants[i+1]*A.bound[i+1]; return 1; } int Locate(Array A,va_list ap,int *off) { //若ap指示的各下标值合法,则求出该元素在A中的而相对位置 *(off)=0; int index; int i; for(i=0;i<A.dim;i++) { index=va_arg(ap,int); if(index<0 || index>=A.bound[i]) return -1; *(off)+=A.constants[i]*index; } return 1; } int Assign_value(Array &A,ElemType e,...) { //A是n维数组,e赋值元素,其后是n个下标值 va_list ap; va_start(ap,e); int off=0; if(Locate(A,ap,&off)<0) return -1; *(A.base+off)=e; return 1; } int Get_value(Array A,ElemType *e,...) { //A是n维数组,e是取值元素,其后是n个下标值 va_list ap; va_start(ap,e); int off=0; if(Locate(A,ap,&off)<0) return -1; *e=*(A.base+off); return 1; } void main() { Array A; Init_Array(A,3,5,6,7); int i,j,k,t=1; for(i=0;i<5;i++) for(j=0;j<6;j++) for(k=0;k<7;k++) Assign_value(A,t++,i,j,k); int value; Get_value(A,&value,3,2,1); printf("数组A的A[3][2][1]的值为:%d\n",value); }
截图如下: