一、数组的结构定义与实现
typedef int ElemType;//定义数组存储数据类型
typedef struct Array
{
ElemType *base; // 数组元素基址,由InitArray分配
int dim; // 数组维数
int *bounds; // 数组维界基址,由InitArray分配
int *constants; // 数组映象函数常量基址,由InitArray分配
}Array,pArray;
二、数组概念示意图
以数组a332为例
三、 数组的操作实现
1、数组操作定义
//1、初始化数组A,可变参数...接收数组每个维度的长度
Status InitArray(Array *A, int dim, ...);
//2、销毁数组A
Status DestroyArray(Array *A);
//3、获取指定数组元素的相对LOC(0,0,...,0)的地址,用off返回
Status Locate(Array A, va_list ap, int *off);
//4、取出指定位置的数组元素值,指定位置用可变参数 ... 接收,数组指定位置的元素值用e返回
Status Value(ElemType *e, Array A, ...);
//5、修改指定位置的数组元素值,修改为e,位置由可变参数 ... 给出
Status Assign(Array *A, ElemType e, ...);
2、数组的操作实现
2.1、初始化数组A,可变参数…接收数组每个维度的长度
假设初始化数组大小为三维,每一维度长度分别为3、3、2
即a332
2.1.1、constants的意义和求法,以行序存储为例
首先由笔记十五可以知道以行序存储的数组元素的查找方法,即如下图所示:以数组Am1m2…mN为例求下标为(j1,j2,…,jN)的元素的地址
以数组Am1m2…mN为例求下标为(j1,j2,…,jN)的元素的地址可写成如下形式
2.1.2、手算constants值,以行序存储为例
Status InitArray(Array *A, int dim, ...)
{
//若维数dim和各维长度合法,则构造相应的数组A,并返回OK
int elemtotal = 1, i; // elemtotal是元素总值
va_list ap;
if (dim<1 || dim>MAX_ARRAY_DIM)//数组维度超出范围
return ERROR;
(*A).dim = dim;//维度赋值
(*A).bounds = (int *)malloc(dim * sizeof(int));//动态分配一个内存空间用于存放每个维度的长度(如a[2][3][4],第一个维度长度为2,第一个维度长度为3,第一个维度长度为4)
if (!(*A).bounds)//如果内存申请失败 即(*A).bounds=NULL,退出程序
exit(OVERFLOW);
va_start(ap, dim);//ap指向函数形参列表中dim后的那个形参,即指向第一个可变参数在堆栈中的地址
for (i = 0; i < dim; ++i)//循环数组维度
{
(*A).bounds[i] = va_arg(ap, int);//取出一个可变形参,接着ap再指向下一个形参,这里形参代表数组的某个维度的长度,即逐一将变长参数赋值给A.bounds[i]
if ((*A).bounds[i] < 0)//如果某个维度的长度小于0,则不符合要求
return UNDERFLOW;
elemtotal *= (*A).bounds[i];//累乘,计算数组中一共有多少个元素 数组元素总数 = 各个维度的长度之积
}
va_end(ap);//释放指针,结束可变参数的使用
(*A).base = (ElemType *)malloc(elemtotal * sizeof(ElemType));//动态分配此数组要占用的总空间
if (!(*A).base)//如果内存申请失败 即(*A).base=NULL,退出程序
exit(OVERFLOW);
(*A).constants = (int *)malloc(dim * sizeof(int));//动态分配数组偏移量基址
if (!(*A).constants)//如果内存申请失败 即(*A).constants=NULL,退出程序
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];//计算每一个维度元素间的偏移量(偏移量单位为“个”以ElemType为单位)
return OK;
}
2.2 、销毁数组A
Status DestroyArray(Array *A)
{
//销毁数组A
if ((*A).base)
{
free((*A).base);
(*A).base = NULL;
}
else
return ERROR;
if ((*A).bounds)
{
free((*A).bounds);
(*A).bounds = NULL;
}
else
return ERROR;
if ((*A).constants)
{
free((*A).constants);
(*A).constants = NULL;
}
else
return ERROR;
return OK;
}
2.3、获取指定数组元素的相对LOC(0,0,…,0)的地址,用off返回
这是一个被调用函数,用于计算指定数组元素的相对位置,注意传递的参数中有va_list ap,因此调用函数参数列表中一定要有可变参数。并且使用了va_start()函数获取了可变参数列表的第一个参数的地址
理解图如下:
其中j1,j2,…,jn 对应等于var1,var2,… varN
Status Locate(Array A, va_list ap, int *off) // Value()、Assign()调用此函数 */
{
//ap指针初始时指向堆栈中可变参数的第一个参数。在这里可变参数的数值代表着每一个维度的第参数个位置,
//即确定了数组中的一个元素如三维数组a[2][3][4]则可变参数分别为为2、3、4初始时ap指向2的地址
//若ap指示的各下标值合法,则求出该元素在A中的相对地址off
int i, ind;
*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;
}
2.4、取出指定位置的数组元素值,指定位置用可变参数 … 接收,数组指定位置的元素值用e返回
Status Value(ElemType *e, Array A, ...) //在VC++中,...之前的形参不能是引用类型
{
//依次为各维的下标值,若各下标合法,则e被赋值为A的相应的元素值
va_list ap;
Status result;
int off;
va_start(ap, A);//获取可变参数堆列表的第一个参数的地址
if ((result = Locate(A, ap, &off)) == OVERFLOW) //调用Locate()获取...位置处的元素相对地址,获取失败返回OVERFLOW
return result;
*e = *(A.base + off);
return OK;
}
2.5、修改指定位置的数组元素值,修改为e
与2、4相似
Status Assign(Array *A, ElemType e, ...)
{
//依次为各维的下标值,若各下标合法,则将e的值赋给A的指定的元素
va_list ap;
Status result;
int off;
va_start(ap, e);
if ((result = Locate(*A, ap, &off)) == OVERFLOW) //调用Locate()获取...位置处的元素相对地址
return result;
*((*A).base + off) = e;//修改指定位置的数组元素值,修改为e
return OK;
}
四、 C语言中可变参数的使用
#include<stdarg.h> // 标准头文件,提供宏va_start,va_arg和va_end, 用于存取变长参数表
参考文章
五、最后给出完整代码
#include<string.h>
#include<malloc.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h> // 标准头文件,提供宏va_start,va_arg和va_end, 用于存取变长参数表
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW 3
#define UNDERFLOW 4
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int Boolean; //Boolean是布尔类型,其值是TRUE或FALSE
typedef int ElemType;
#define MAX_ARRAY_DIM 8 // 假设数组维数的最大值为8
typedef int ElemType;//定义数组存储数据类型
typedef struct Array
{
ElemType *base; // 数组元素基址,由InitArray分配
int dim; // 数组维数
int *bounds; // 数组维界基址,由InitArray分配
int *constants; // 数组映象函数常量基址,由InitArray分配
}Array,pArray;
//1、初始化数组A,可变参数...接收数组每个维度的长度
Status InitArray(Array *A, int dim, ...);
//2、销毁数组A
Status DestroyArray(Array *A);
//3、获取指定数组元素的相对LOC(0,0,...,0)的地址,用off返回
Status Locate(Array A, va_list ap, int *off);
//4、取出指定位置的数组元素值,指定位置用可变参数 ... 接收,数组指定位置的元素值用e返回
Status Value(ElemType *e, Array A, ...);
//5、修改指定位置的数组元素值,修改为e,位置由可变参数 ... 给出
Status Assign(Array *A, ElemType e, ...);
int main()
{
Array A;
int i, j, k, *p, dim = 3, bound1 = 3, bound2 = 4, bound3 = 2; //a[3][4][2]数组
ElemType e, *p1;
InitArray(&A, dim, bound1, bound2, bound3); //构造3*4*2的3维数组A
p = A.bounds;
printf("A.bounds=");
for (i = 0; i < dim; i++) //顺序输出A.bounds
printf("%d ", *(p + i));
p = A.constants;
printf("\nA.constants=");
for (i = 0; i < dim; i++) //顺序输出A.constants
printf("%d ", *(p + i));
printf("\n%d页%d行%d列矩阵元素如下:\n", bound1, bound2, bound3);
for (i = 0; i < bound1; i++)
{
for (j = 0; j < bound2; j++)
{
for (k = 0; k < bound3; k++)
{
Assign(&A, i * 100 + j * 10 + k, i, j, k); // 将i*100+j*10+k赋值给A[i][j][k]
Value(&e, A, i, j, k); //将A[i][j][k]的值赋给e
printf("A[%d][%d][%d]=%2d ", i, j, k, e); //输出A[i][j][k]
}
printf("\n");
}
printf("\n");
}
p1 = A.base;
printf("A.base=\n");
for (i = 0; i < bound1*bound2*bound3; i++) //顺序输出A.base
{
printf("%4d", *(p1 + i));
if (i % (bound2*bound3) == bound2 * bound3 - 1)
printf("\n");
}
DestroyArray(&A);
return 0;
}
//1、初始化数组A
Status InitArray(Array *A, int dim, ...)
{
//若维数dim和各维长度合法,则构造相应的数组A,并返回OK
int elemtotal = 1, i; // elemtotal是元素总值
va_list ap;
if (dim<1 || dim>MAX_ARRAY_DIM)//数组维度超出范围
return ERROR;
(*A).dim = dim;//维度赋值
(*A).bounds = (int *)malloc(dim * sizeof(int));//动态分配一个内存空间用于存放每个维度的长度(如a[2][3][4],第一个维度长度为2,第一个维度长度为3,第一个维度长度为4)
if (!(*A).bounds)//如果内存申请失败 即(*A).bounds=NULL,退出程序
exit(OVERFLOW);
va_start(ap, dim);//ap指向函数形参列表中dim后的那个形参,即指向第一个可变参数在堆栈中的地址
for (i = 0; i < dim; ++i)//循环数组维度
{
(*A).bounds[i] = va_arg(ap, int);//取出一个可变形参,接着ap再指向下一个形参,这里形参代表数组的某个维度的长度,即逐一将变长参数赋值给A.bounds[i]
if ((*A).bounds[i] < 0)//如果某个维度的长度小于0,则不符合要求
return UNDERFLOW;
elemtotal *= (*A).bounds[i];//累乘,计算数组中一共有多少个元素 数组元素总数 = 各个维度的长度之积
}
va_end(ap);//释放指针,结束可变参数的使用
(*A).base = (ElemType *)malloc(elemtotal * sizeof(ElemType));//动态分配此数组要占用的总空间
if (!(*A).base)//如果内存申请失败 即(*A).base=NULL,退出程序
exit(OVERFLOW);
(*A).constants = (int *)malloc(dim * sizeof(int));//动态分配数组偏移量基址
if (!(*A).constants)//如果内存申请失败 即(*A).constants=NULL,退出程序
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];//计算每一个维度元素间的偏移量(偏移量单位为“个”以ElemType为单位)
return OK;
}
//2、销毁数组A
Status DestroyArray(Array *A)
{
//销毁数组A
if ((*A).base)
{
free((*A).base);
(*A).base = NULL;
}
else
return ERROR;
if ((*A).bounds)
{
free((*A).bounds);
(*A).bounds = NULL;
}
else
return ERROR;
if ((*A).constants)
{
free((*A).constants);
(*A).constants = NULL;
}
else
return ERROR;
return OK;
}
//3、获取指定数组元素的相对LOC(0,0,...,0)的地址,用off返回
Status Locate(Array A, va_list ap, int *off) // Value()、Assign()调用此函数 */
{
//ap指针初始时指向堆栈中可变参数的第一个参数。在这里可变参数的数值代表着每一个维度的第参数个位置,
//即确定了数组中的一个元素如三维数组a[2][3][4]则可变参数分别为为2、3、4初始时ap指向2的地址
//若ap指示的各下标值合法,则求出该元素在A中的相对地址off
int i, ind;
*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;
}
//4、取出指定位置的数组元素值,指定位置用可变参数 ... 接收,数组指定位置的元素值用e返回
Status Value(ElemType *e, Array A, ...) //在VC++中,...之前的形参不能是引用类型
{
//依次为各维的下标值,若各下标合法,则e被赋值为A的相应的元素值
va_list ap;
Status result;
int off;
va_start(ap, A);//获取可变参数堆列表的第一个参数的地址
if ((result = Locate(A, ap, &off)) == OVERFLOW) //调用Locate()获取...位置处的元素相对地址,获取失败返回OVERFLOW
return result;
*e = *(A.base + off);
return OK;
}
//5、修改指定位置的数组元素值,修改为e
Status Assign(Array *A, ElemType e, ...)
{
//依次为各维的下标值,若各下标合法,则将e的值赋给A的指定的元素
va_list ap;
Status result;
int off;
va_start(ap, e);
if ((result = Locate(*A, ap, &off)) == OVERFLOW) //调用Locate()获取...位置处的元素相对地址
return result;
*((*A).base + off) = e;//修改指定位置的数组元素值,修改为e
return OK;
}