• 数组的实现——数据结构


      由于存储单元是一维的结构,而数组是个多维的结构,则用一组连续的存储单元存放数据元素就有次序约定的问题了。

      假设现在有一个三维数组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>,用于获取不确定个数的参数

    用法
    (1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
    (2)然后用VA_START宏初始化刚定义的VA_LIST变量;
    (3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
    (4)最后用VA_END宏结束可变参数的获取。
    宏:
      VA_START宏:获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):
                            #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
      VA_ARG宏:   获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):
                            #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
      VA_END宏: 清空va_list可变参数列表:
                            #define va_end(ap) ( ap = (va_list)0 )
    测试如下:
    #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
    ", 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
    ",value);
    }
    

    截图如下:

            

  • 相关阅读:
    那么如何设计接口才能做到幂等?
    使用FluentValidation和MediatR实现接口请求验证
    虎年 云原生落地技术趋势
    使用 Dapr 缩短软件开发周期,提高工作效率
    .NET 诞生已20周年,您的 .NET 技能是否还停留在2010 年?
    Mono创始人 Miguel de Icaza今天离开微软
    在 Azure 上构建和部署云原生应用程序和容器化应用程序
    2021 中国.NET开发者峰会近50场热点技术专题揭秘
    Blazor 和 TypeScript 互操作工具库
    Dapr集成之GRPC 接口
  • 原文地址:https://www.cnblogs.com/helloworldcode/p/6942050.html
Copyright © 2020-2023  润新知