数组的顺序存储表示(C语言数据结构)
简单介绍一下<stdarg.h>
的作用
stdarg.h
是C语言标准库头文件之一,用于支持变长参数函数。stdarg.h
中定义了三个宏:va_start
、va_arg
和va_end
,这些宏可以帮助我们在变长参数函数中访问传递给函数的参数列表。以下是三个宏的详细解释:
1.va_start(ap, param)
:该宏用于初始化一个指向参数列表的指针ap
,以便后续使用a_arg
宏访问参数。ap
是一个va_list
类型的变量,param
是传递给函数的最后一个固定参数的参数名。va_start
宏必须在使用va_arg
宏之前调用。
2.va_arg(ap, type)
:该宏用于访问参数列表中的下一个参数。ap
是指向参数列表的指针,type是要访问的参数的类型。va_arg
宏返回一个值,该值表示参数列表中的下一个参数,并将指针ap
移动到下一个参数的位置。在使用va_arg
宏之前,必须调用va_start
宏初始化ap
指针。
3.va_end(ap)
:该宏用于结束对参数列表的访问。ap
是指向参数列表的指针,该指针必须是通过调用va_start
宏初始化的。调用va_end
宏后,指针ap
将无效,不能再用于访问参数列表。
下面是一个示例程序,演示如何使用stdarg.h
中的宏:3
#include <stdarg.h>
#include <stdio.h>
void print_ints(int num, ...) {
va_list args;
va_start(args, num); // 初始化指向参数列表的指针
for (int i = 0; i < num; i++) {
int val = va_arg(args, int); // 获取下一个整数参数
printf("%d ", val);
}
va_end(args); // 结束对参数列表的访问
}
int main() {
print_ints(3, 1, 2, 3); // 输出:1 2 3
return 0;
}
数组的顺序存储表示和实现
由于数组一般不作插入和删除操作,也就是说,一旦建立了数组,则结构中的数据元素个数和元素之间的关系就不再发生改变。因此,采用顺序存储结构表示数组是自然的。
对于数组,一旦规定了它的维数和各维的长度,便可以为它分配存储空间。反之,只要给出一组下标,便可求得相应数组元素的存储位置。
假设每个数据元素占L个存储单元,则二维数组A中任意元素
a
i
j
a_{ij}
aij的存储位置就可由下式确定:
L
O
C
(
i
,
j
)
=
L
O
C
(
0
,
0
)
+
(
b
2
∗
i
+
j
)
L
LOC(i, j) = LOC(0, 0) + (b_2*i+j)L
LOC(i,j)=LOC(0,0)+(b2∗i+j)L其中,
a
(
0
,
0
)
a(0, 0)
a(0,0)就是二维数组A的起始存储位置,也称为基地址或基址。
推广到一般情况,可得到n维数组的数据元素存储位置的计算公式:
L
(
j
1
,
j
2
,
.
.
.
,
j
n
)
=
L
O
C
(
0
,
0
,
.
.
.
,
0
)
+
(
b
2
∗
b
3
∗
.
.
.
∗
b
n
∗
j
2
+
.
.
.
+
b
n
∗
j
n
−
1
+
j
n
)
L(j_1, j_2,...,j_n) = LOC(0, 0,...,0)+(b_2*b_3*...*b_n*j_2+...+b_n*j_{n-1} + j_n)
L(j1,j2,...,jn)=LOC(0,0,...,0)+(b2∗b3∗...∗bn∗j2+...+bn∗jn−1+jn)
=
L
O
C
(
0
,
0
,
.
.
.
0
,
)
+
(
∑
i
=
1
n
−
1
j
i
∏
k
=
i
+
1
n
b
k
+
j
n
)
L
= LOC(0,0,...0,) + (\sum^{n-1}_{i=1}j_i\prod^{n}_{k=i+1}b_k+j_n)L
=LOC(0,0,...0,)+(i=1∑n−1jik=i+1∏nbk+jn)L上式称为n维数组的映像函数。可以看出,一旦确定了数组的各维的长度,就可以很方便的确定元素的位置。由于计算各个元素的存储位置的时间相等,所以存取数组中任意元素的时间也相等。我们称具有这一特点的存储结构为随机存储结构。
/*
本文件完成数组的顺序存储表示
*/
#include<stdarg.h>
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#define MAX_ARRAY_DIM 8 //数组维数的最大值为8
#define ElemType int
typedef struct {
ElemType* base; //数组元素的基地址,由InitArray分配
int dim; //数组维数
int* bounds; //数组维界基址(简单说就是每一维所能存储的元素的个数),由InitArray分配
int* constants; //数组映像函数常数基址,由InitArray分配
}Array;
bool InitArray(Array* A, int dim, ...);
bool DestroyArray(Array* A);
bool Value(Array* A, ElemType* e, ...);
bool Assign(Array* A, ElemType e, ...);
int main() {
Array A;
int dim = 3;
int a = 3, b = 4, c = 5;
if (InitArray(&A, dim, a, b, c)) {
printf("初始化数组成功\n");
} else {
printf("初始化数组失败\n");
}
int e = 0;
Assign(&A, 4, 1, 3, 3);
Value(&A, &e, 1, 3, 3);
printf("dim:%d\n",A.dim);
printf("bounds:%d %d %d\n", A.bounds[0], A.bounds[1], A.bounds[2]);
printf("constans:%d %d\n", A.constants[0], A.constants[1]);
printf("%d", e);
return 0;
}
/// @brief 初始化一个数组
/// @param A 即将被初始化的数组
/// @param dim 数组的维数
/// @param ... 各个维的所能存储的元素的个数
/// @return 如果初始化成功则返回true
bool InitArray(Array* A, int dim, ...) {
//判断输入的维数是否合法
if (dim < 1 || dim > MAX_ARRAY_DIM) { printf("你输入维数的有毛病吧"); return false; }
A->dim = dim;
int* bound = (int*)malloc(8 * sizeof(int));
if (!bound) { printf("这次不是你的问题,是系统分配内存的问题"); return false; }
A->bounds = bound;
//输入每一维所能容纳的元素的个数
va_list ap;
va_start(ap, dim);
int ElemTotal = 1;
for (int i = 0; i < A->dim; i++) {
A->bounds[i] = va_arg(ap, int);
if (A->bounds[i] < 0) { printf("元素个数输入的有毛病"); return false; }
ElemTotal *= A->bounds[i];
}
va_end(ap);
A->base = (ElemType*)malloc(ElemTotal * sizeof(ElemType));
if (!A->base) { printf("这次不是你的问题,是系统分配内存的问题"); return false; }
//初始化数组映像函数常数基址
A->constants = (int*)malloc((A->dim) * sizeof(int));
if (!A->constants) { printf("这次不是你的问题,是系统分配内存的问题"); return false; }
int total = 1;
for (int j = 1; j < A->dim; j++) {
total *= A->bounds[j];
}
A->constants[0] = total;
for (int i = 1; i < A->dim; i++) {
A->constants[i] = total / A->bounds[i];
}
return true;
}
/// @brief 销毁数组
/// @param A 即将被销毁的数组
/// @return 似乎只能返回true
bool DestroyArray(Array* A) {
free(A->base);
free(A->bounds);
free(A->constants);
return true;
}
/// @brief 将e赋值为所指定的A的元素值
/// @param A 指定的数组
/// @param e 将被赋值的变量
/// @param ... 元素的下标
/// @return 如果各下标不越界,将e赋值成功后,返回true
bool Value(Array* A, ElemType* e, ...) {
int total = 0;
va_list ap;
va_start(ap, e);
for (int i = 0; i < A->dim; i++) {
int index = va_arg(ap, int);
if (index < 0 || index > A->bounds[i]) { printf("你怕是有点越界了"); return 0; }
total += index * A->constants[i];
}
*e = *(A->base + total);
return true;
}
/// @brief 若下标不越界,则将e赋值给所指定的A的元素
/// @param A 即将被赋值的Array
/// @param e
/// @param ... 下标
/// @return 如果赋值成功则返回true
bool Assign(Array* A, ElemType e, ...) {
int total = 0;
va_list ap;
va_start(ap, e);
for (int i = 0; i < A->dim; i++) {
int index = va_arg(ap, int);
if (index < 0 || index > A->bounds[i]) { printf("你怕是有点越界了"); return 0; }
total += index * A->constants[i];
}
*(A->base + total) = e;
return true;
}