第五章 数组的数组的顺序存储表示和实现

来源:互联网 发布:mac照片上传icloud 编辑:程序博客网 时间:2024/05/16 15:48

代码能够实现但是会出现非法访问地址。

// Array.cpp : Defines the entry point for the console application.

//稀疏矩阵三元组顺序表示的基本操作的实现并完成快速转置
//---------------------------------------------------
//----------------数组的顺序存储表示和实现-------------------------
#include <iostream>
#include <stdarg.h>//标准头文件,提供宏va_start,va_arg和va_end,用于存取变长参数表
#include <stdlib.h>
#include<malloc.h>
#include<stdio.h>
//======================================================


#define MAX_ARRAY_DIM 8  //数组的最大维数
#define UNDERFLOW 1
#define OVERFLOW 2
#define ERROR 1
#define OK 4
typedef int Status;
typedef int ElemType;
va_list  ap;


typedef struct{
   ElemType *base;      //数组元素基址,
   int    dim;          //数组维数
   int    *bounds;      //数组维界基址
   int    *constants;   //数组映象函数常量基址
}Array;
//-------------------基本操作的函数原型说明----------------
/*InitArray(Array &A, int dim, int a,int b,int c );// 若维数dim和随后的各维长度数合法,构造相应的数组,并返回OK
DestroyArray (Array &A);//销毁数组A
Value(Array A, ElemType &e,int a,int b,int c);//若各下标不超界,则e赋值为所指定的A的元素值,并返回OK
Assign(Array &A, ElemType e,int a,int b,int c);*///若各下标不超界,则将e的值赋给所指定的A的元素,并返回OK


//-------------------基本操作的算法描述--------------------
//---------------------构造数组----------------- ------------
Status InitArray (Array &A, int dim,...)
{
int elemtotal;
if(dim<1 ||dim>MAX_ARRAY_DIM) return ERROR;
A.dim=dim;
A.bounds=(int *)malloc(dim*sizeof(int));
if(!A.bounds) exit(OVERFLOW);
//若各维长度合法,则存入A.bounds,并求出A的元素总数elemtotal
elemtotal=1;
va_start(ap,dim) ;  //ap为va_list类型,是存放变长参数表信息的数组
for(int i=0;i<dim;i++){
A.bounds[i]=va_arg(ap,int);
if(A.bounds[i]<0) return UNDERFLOW;
elemtotal*=A.bounds[i];
}
va_end(ap);
A.base=(ElemType *)malloc(elemtotal*sizeof(ElemType));
if(!A.base) exit(OVERFLOW);
//求映象函数的常数ci,并存入A.constant[i-1],i=1,...,dim
A.constants =(int *)malloc(dim*sizeof(int));
if(!A.constants) exit(OVERFLOW);
A.constants[dim-1]=1;  //L=1,指针的增减以元素的大小为单位
for(int i=dim-2;i>=0;--i)
A.constants[i]=A.bounds[i+1]*A.constants[i+1];
return OK;
} //InitArray


//-------------------销毁数组--------------------------
Status DestroyArray(Array &A){
   if(!A.base) return ERROR;
   free(A.base); A.base=NULL;


   if(!A.bounds) return ERROR;
   free(A.bounds); A.bounds=NULL;


   if(!A.constants) return ERROR;
   free(A.constants); A.constants=NULL;
}




//------------------求元素在A中的相对位置------------------------
Status Locate(Array A, va_list ap, int &off){
   //若ap指示的各下标值合法,则求出该元素在A中的相对地址off
   off=0;
   for(int i=0; i<A.dim; ++i)
    {
  int ind;
      ind=va_arg(ap, int);
      if(ind<0 || ind>=A.bounds[i])  return OVERFLOW;
      off+=A.constants[i]*ind;
     }
 return OK;
}//Locate




//------------------------取数组元素的值-----------------------
Status Value(Array A, ElemType &e,...){
int off;
int result;
   va_start(ap, e);
   if((result=Locate(A, ap, off))<=0)  return result;
   e=*(A.base + off);
   return OK;
}//Value
//----------------数组元素赋值-------------------------------
Status Assign(Array &A, ElemType e,...){
int result;
int off;
   va_start(ap,e);
   if((result=Locate(A,ap,off))<=0) return result;
   *(A.base+off)=e;
   return OK;


}//Assign
main()
{


Array A;
int i,j,k,e;
InitArray(A,3,3,4,2);
printf("给元素赋值!");
for(i=0;i<3;i++)
for(j=0;j<4;j++)
for(k=0;k<2;k++)
Assign(A,i*100+j*10+k,i,j,k);
   printf("三页四行两列的矩阵输出如下!");


for(i=0;i<3;i++)
{
printf("\n");
for(j=0;j<4;j++)
{
              printf("\n");


for(k=0;k<2;k++)
{
Value(A,e,i,j,k);
e=i*100+j*10+k;
printf("A[%d][%d][%d]=%d",i,j,k,e);
}
}
   printf("A.*base=%d",A.base);
   printf("A.dim=%d",A.dim);
           printf("A.*bounds=%d",A.bounds);
           printf(" A.*constants=%d",A.constants);
           DestroyArray(A);
           printf("A.*base=%d",A.base);
   printf("A.dim=%d",A.dim);
           printf("A.*bounds=%d",A.bounds);
           printf(" A.*constants=%d",A.constants);
   printf("感谢您的使用,按Enter键退出!");
}

}

大家可能对上面的bounds和constants代表的含义不太理解,我从网上找了一些解释。

#include<stdarg.h>         
#define MAX_ARRAY_DIM 8     //假设数组维数的最大值为8
typedef struct {
    ElemType *base;         //数组元素基址,由InitArray分配
    int dim;                //数组维数
    int *bounds;            //数组维界基址,由InitArray分配
    int *constants;         //数组映象函数常量基址,由InitArray分配
}Array;

Status InitArray(Array &A,int dim,...){
//若维数dim和随后的各维长度合法,则构造相应的数组A,并返回OK。
if (dim<1 ||dim>MAX_ARRAY_DIM)return ERROR;
A.dim=dim;
A.bounds=(int *)malloc(dim*sizeof(int));
if (!A.bounds) exit(OVERFLOW);
//若各维长度合法,则存入A.bounds,并求出A的元素总数elemtotal。
elemtotal=1;
va_start(ap,dim);    //ap为va_list类型,是存放变长参数表信息的数组。
for (i=0;i<dim;++i){
    A.bounds[i]=va_arg(ap,int);
    if (A.bounds[i]<0)return UNDERFLOW;
    elemtotal * = A.bounds[i];
}
va_end(ap);
A.base=(ElemType *)malloc(elemtotal *sizeof(ElemType));
if (!A.base) exit (OVERFLOW):
//求映象函数的常数ci(i为下标),并存入A.constants[i-1],i=1,...dim。
A.constants=(int *)malloc(dim *sizeof(int));
if (!A.constants)exit (OVERFLOW);
A.constants[dim-1]=1;
for (i=dim-2;i>=0;--i)
  A.constants[i]=A.bounds[i+1] * A.constants[i+1];
return OK;
}

status Locate(Array A,va_list ap,int &off){
//若ap指示的各下标值合法,则求出该元素在A中相对地址off。
   off=0;
   for (i=0;i<A.dim;++i){
        ind=va_arg(ap,int);
        if (ind<0 || ind>=A.bounds[i])return OVERFLOW;
        off + = A.constants[i] * ind;
   }
   return OK;
请问各位前辈,数组维界基址,即代码中的A.bounds,是用来储存什么的?而A.constants又是用来储存什么的?查询过相关资料,还是不明白。尤其是代码中的
A.constants[dim-1]为什么要把1,赋给它?
希望各位前辈帮忙解答一下,谢谢。
我只把我看懂了的部分说出来,若有不周,请多指教。

#include<stdarg.h>         
#define MAX_ARRAY_DIM 8     //假设数组维数的最大值为8
typedef struct {
    ElemType *base;         //数组元素基址,由InitArray分配
    int dim;                //数组维数
    int *bounds;            //数组维界基址,由InitArray分配
    int *constants;         //数组映象函数常量基址,由InitArray分配
}Array;

Status InitArray(Array &A,int dim,...){//这里用的是“可变参”形参方式。它主要解决维数不定的问题。
//举例:设有4维数组,各维分别是:4,5,6,7(这些数字是随意给的),那么,调用方式:
//InitArray(ar, 4, 4, 5, 6, 7);
//ar其中,ar也是假设的变量名称, 4表示数组有4维, 4, 5, 6, 7这4个数是各维大小
//如果是5维的,那么就这样:
//InitArray(ar, 5, 第一维数,第二维数,第三维数,第四维数,第五维数);
//若维数dim和随后的各维长度合法,则构造相应的数组A,并返回OK。
if (dim<1 ||dim>MAX_ARRAY_DIM) return ERROR;
A.dim=dim;
A.bounds=(int *)malloc(dim*sizeof(int));
if (!A.bounds) exit(OVERFLOW);
//若各维长度合法,则存入A.bounds,并求出A的元素总数elemtotal。
elemtotal=1;
va_start(ap,dim);    //ap为va_list类型,是存放变长参数表信息的数组。
for (i=0;i<dim;++i){
    A.bounds[i]=va_arg(ap,int);//从这里可以看出,A.bounds数组中,存放的是各维的大小
    if (A.bounds[i]<0) return UNDERFLOW;
    elemtotal * = A.bounds[i];//各维数之积,自然是数组中元素的总个数
}
va_end(ap);
A.base=(ElemType *)malloc(elemtotal *sizeof(ElemType));//这个就是“多维数组”的存储本质:一维数组!
//用一维方式表示多维数组后(其实,从管理和使用的角度看,内存就只有一维这么一种形式),存在如何按“多维”的逻辑角度定位元素的问题。再说清楚些:假设前面所讲的4维数组,其元素用下标形式表示,范围为:(0,0,0,0)到(3,4,5,6)。对于任意下标(在有效范围内)(i1, i2, i3, i4)所对应的元素,转换到“一维”空间后,其下标应该是什么?这就是这个程序后面要处理的主要问题。
if (!A.base) exit (OVERFLOW):
//求映象函数的常数ci(i为下标),并存入A.constants[i-1],i=1,...dim。
A.constants=(int *)malloc(dim *sizeof(int));
if (!A.constants)exit (OVERFLOW);
//以前面的4维数组为例子,其中A.bounds[0]=4,A.bounds[1]=5,A.bounds[2]=6,A.bounds[3]=7。
//跟踪下面的程序:
A.constants[dim-1]=1;//A.constants[3] = 1
for (i=dim-2;i>=0;--i)//A.constants[2] = 7,A.constants[1] = 6*7,A.constants[0] = 5*6*7
  A.constants[i]=A.bounds[i+1] * A.constants[i+1];
//说到这里,这个问题就清晰了:A.constants中的元素,是帮助定位用的。比如说:对于(2,0,0,0)这个下标的元素,应该越过前面的(0,0,0,0)~(0,4,5,6)和(1,0,0,0)~(1,4,5,6)这两大块,而这两大块中的每一块都有5*6*7个元素,这正好就是A.constants[0]中所存放的数据啊!
//现在应该明白了吧!
return OK;
}

status Locate(Array A,va_list ap,int &off){
//若ap指示的各下标值合法,则求出该元素在A中相对地址off。
   off=0;
   for (i=0;i<A.dim;++i){
        ind=va_arg(ap,int);
        if (ind<0 || ind>=A.bounds[i]) return OVERFLOW;
        off + = A.constants[i] * ind;
   }
   return OK;
非常感谢啊。。。
但是,还有一点我仍然不明白。就是为什么A.constants[dim-1]永远都等于1呢?
好像明白一点了(时间是有些长了)。原因有两个:
1、因为程序里是通过乘法进行计算的,所以初值必须为1;
2、(这是关键)在具体定位时,最后那一维其实直接取决于下标。假设有下标:(3, 2, 2, 5),那么定位公式应该是:
t = 3*A.constants[0] + 2*A.constants[1] + 2*A.constans[2] + 5*A.constans[3]//因为A.constans[3]的值是1,所以可以不用写;写上也有一个好处:利于循环体的一致性。

另外一个解释:

bounds存的就是每一维里面的个数,constants保存的是每一个维度如果下标增加1,那个对应到内存空间的下标应该增加多少。说起来比较抽象,我们假设是3维,就比较容易说清楚了,首先把3维看作有bounds[0]那么高,对于每一个0到bounds[0]-1的范围内,就是一个平面,这个平面有bounds[1]那么长,bounds[2]那么宽。那么,我们把高=0,长=0,宽=0对应到内存的第一个位置,高=0,长=0,宽=1的对应到第二个位置,那么高=0,长=1,宽=0应该放在什么位置呢?显然就是0+bounds[2]这个位置。那么高=1,长=0,宽=0的那个元素应该在哪个位置呢?显然是高=0这一个平面放完了之后的那个位置,高=0这个平面有长度*宽度那么多个元素,也就是bounds[1]*bounds[2]这么多个元素,所以高=1,长=0,宽=0这个元素就应该在0+bounds[1]*bounds[2]这个位置,对吧。假设还有第四维度,我们假设这个维度代表时间吧,那时间=0,高=0,长=0,宽=0的元素放在内存第0个位置,那么时间=1,高=0,长=0,宽=0的元素是不是应该放在0+bound[1]*bound[2]*bound[3]这个位置呢。这就是A.constants[i]=A.bounds[i+1] * A.constants[i+1];这个公式的来历。当然,我只是很简单的解释了,很多细节需要你自己考虑,因为语言表示起来太复杂了,不知道怎么表述。。。
其实你仔细看A.constants[i]=A.bounds[i+1] * A.constants[i+1];,这是一个递推公式,把它展开的话,下面我就把constants[i]简写为coni,bounds[i]简写为boni那么con i= bon[i+1]*con[i+1]=bon[i+1]*bon[i+2]*con[i+2] = bon[i+1]*bon[i+2]*bon[i+3]*con[i+3]
=bon[i+1]*bon[i+2]*bon[i+3]*...*bon[dim]你看这个公式是不是就是相当于上面说的高度*长度*宽度? 刚才那个bon[dim]应该写成bon[dim-1]不过这个不影响理解。
然后我们看最后一维,例如上面例子的宽度,宽度+1是不是就正好内存地址+1呢?于是对应宽度这个最后的维度,每次地址只需+1就能访问下一个元素,因此bon[dim-1]也就是最后一维的,是不是就应该等于1呢。。。于是,上面的程序就能够搞懂了吧。
 

0 0
原创粉丝点击