写给自己的话:在之前写代码的过程中,我还没有遇到过要自己设计一个可变参数的函数。但是,数据结构中多维数组中,我就不得不去面对这个难题了。说实话,我一开始没有打算写这篇博客的。但是,我看到我的同学(我之前和他讲了这个代码)还在深入研究这个可变参数,我觉得自己也得把它弄得清楚一些,可不能半吊子了!
回归正题,相信大家书本上一定会给大家介绍四个类型va_list,va_start,va_arg,va_end。那么他们分别怎么去用捏!
首先,你的有一个va_list的变量,就像这样:va_list ap;你可以把它想成一个list集合(当然这样是不准确的),你的的不确定的变量就会存到这个va_list中。
其后,你得给ap赋值,不然他怎么会知道你的变量是哪几个。所以:va_start(va,dim);这里的dim是你输入的维度大小,是一个确定的参数。这里可能会有些不理解,等会我解释的,这里就先这样理解一哈。
刚刚把多个变量存到了va_list中,那么现在是不是要把它读取出来了。所以:ElementType i=va_arg(ap,ElementType);这里的ElemeentType是你自己变量的类型。通常这条语句是要放到一个for中去的,因为你变量的个数多半是多个。
最后,当你读取完毕后,作为一个有一个好的编程习惯,受过良好训练(怎么有点像狗)的学生来说,此时应该结束读取,所以:va_end(ap);到这里,基本上这些个变量的用法应该会了,如果你不想深入了解的话,就不用往下看了。
首先,先看一下这四个类型的定义,即看看源代码是怎么写的。
// stdarg.h
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
// vadefs.h
typedef char * va_list;
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
#define _ADDRESSOF(v) ( &(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
Note:
好了,写了这么多,其实还是有一些底层是我现在弄不清楚的。所以捏,我等到学到了更多的底层知识再来补充这个文章。也欢迎大家在下方留言,我们一起讨论!
这是数据结构中实现多维数组的代码,注释都是我自己的理解,大家可以看看:是在vs2017下运行的,可能在你的编译器上没有pch.h文件,那就直接去掉它即可。如果运行后有一个nullptr无法识别的问题,就将nullptr改成NULL就好了。
这个是main.cpp
#include "pch.h"
#include"statement.h"
#include
int main()
{
Array A;
InitArray(A, 3, 2,2,2);
for (int i = 0; i < 2*2*2; i++)
{
A.base[i] = i;
}
int e = 0;
Value(e, A, 1,1,1);
printf("%d", e);
return 0;
}
这个是statement.h
#pragma once
#ifndef STATEMENT_H
#define STATEMENT_H
#include
#define MAX_ARRAY_DIM 8
typedef int Element;
typedef struct
{
Element* base;
int dim;
int* bounds;
int* constants;
}Array;
//如果维数合法,并且各维长度也合法,则构造相应的数组A
void InitArray(Array& A, int dim, ...);
//销毁数组
void DestroyArray(Array& A);
//求得数组元素中的某一元素的地址值
int Locate(Array A, va_list ap, int& off);
//将数组的值赋给e
int Value(Element& e, Array A, ...);
//将e的值赋值给指定的数组元素下标
int Assign(Array& A, Element e, ...);
#endif // !STATEMENT_H
这个是method.cpp
#include"pch.h"
#include"statement.h"
#include
#include
#include
//start**
void InitArray(Array& A, int dim, ...)
{
//计算数组元素的个数
int eletotal = 1;
//判断dim是否合法
if (dim < 1 || MAX_ARRAY_DIM < dim)
{
printf("构建数组维度不合法\n");
return;
}
A.dim = dim;
/*
例如,你要一个3维的数组(比较好理解,就和一页纸一样)
你就会首先输入一个3,再输入这3维的分别的大小
*/
//bounds就是用来存贮每一维大小,例如,第0维大小为5,那么A.bounds[0]=5;
A.bounds = (int*)malloc(sizeof(int)*A.dim);
if (A.bounds == nullptr)
{
printf("bounds地址分配空间失败\n");
exit(-1);
}
va_list ap;
va_start(ap, dim);
for (int i = 0; i < dim; i++)
{
A.bounds[i] = va_arg(ap, int);
if (A.bounds[i] < 0)
{
printf("有一维数据个数计数失败\n");
exit(0);
}
eletotal *= A.bounds[i];//例如Array[5][6]不就是有30个元素吗,Array[3][5][6]就有3*5*6个元素
}
va_end(ap);
//base是第一个基地址(分配的空间足够容纳该维度所有元素)
A.base = (Element*)malloc(sizeof(Element)*eletotal);
if (A.base == nullptr)
{
printf("base地址分配失败\n");
exit(-1);
}
//求出映像函数的常数Ci,并存入A.constants[i-1],i=1...dim
//求的是每一维的基地址
/*
至于这里为什么要倒着求,你可以自己去举一个例子。
int array[5][4][3]这样一个数组,array[0][0][0]=SA,求array[2][3][1]的地址S的值。
S=(2*4*3+3*3+1)*4+SA;再把这个步骤和Locate()对比一下就好了!
*/
A.constants = (int*)malloc(dim * sizeof(int));
if (A.constants == nullptr)
{
printf("constants地址分配失败\n");
exit(-1);
}
A.constants[dim - 1] = 1;//这里是假设元素的字节为1
/*
再看一个三维数组A(3,4,5):
bounds[] = {3,4,5}
constants[] = {4*5, 5, 1}
可以看出:constants[2] = 1; constants[1] = constants[2]*bounds[2]; constants[0] = constants[1]*bounds[1]
即:constants[i] = constans[i+1] * bounds[i+1]
对于一个元素, 其第0维每增加一, 在线性结构L中的位置就增加了4*5 = 20个
*/
for (int i = dim - 2; i >= 0; i--)
{
A.constants[i] = A.bounds[i + 1] * A.constants[i + 1];
}
}
//end**
//start**
int Locate(Array A, va_list ap, int& off)
{
off = 0;//防止off一开始有初值,所以先初始化为0
int ind = 0;
for (int i = 0; i < A.dim; i++)//这样写的话就符合用户输入维数的习惯(用户觉得维数从一开始)
{
ind = va_arg(ap, int);
if (ind<=0 || ind>A.bounds[i])
{
printf("读取数组维度的个数失败\n");
return -1;
}
off += A.constants[i] * (ind-1);
}
return 1;
}
//end**
//start**
int Value(Element& e, Array A, ...)
{
va_list ap;
int result = 0;
int off = 0;
va_start(ap, A);
if (-1 == (result = Locate(A, ap, off)))
{
printf("错误\n");
return result;
}
e = *(A.base + off);
return 0;
}
//end**
//start**
int Assign(Array& A, Element e, ...)
{
va_list ap;
int off = 0;
int result = 0;
va_start(ap, e);
if (-1 == (result = Locate(A, ap, off)))
{
printf("错误\n");
return result;
}
*(A.base + off) = e;
return 1;
}
//end**
//start**
void DestroyArray(Array& A)
{
if (A.base)
{
free(A.base);
A.base = nullptr;
}
if (A.bounds)
{
free(A.bounds);
A.bounds = nullptr;
}
if (A.constants)
{
free(A.constants);
A.constants = nullptr;
}
}
//end**
本次参考的大佬的博客:
http://www.cppblog.com/maosher/archive/2010/06/17/118126.aspx
https://www.cnblogs.com/chinazhangjie/archive/2012/08/18/2645475.html