数据结构-多维数组的实现

写给自己的话:在之前写代码的过程中,我还没有遇到过要自己设计一个可变参数的函数。但是,数据结构中多维数组中,我就不得不去面对这个难题了。说实话,我一开始没有打算写这篇博客的。但是,我看到我的同学(我之前和他讲了这个代码)还在深入研究这个可变参数,我觉得自己也得把它弄得清楚一些,可不能半吊子了!
回归正题,相信大家书本上一定会给大家介绍四个类型va_list,va_start,va_arg,va_end。那么他们分别怎么去用捏!

  1. 首先,你的有一个va_list的变量,就像这样:va_list ap;你可以把它想成一个list集合(当然这样是不准确的),你的的不确定的变量就会存到这个va_list中。

  2. 其后,你得给ap赋值,不然他怎么会知道你的变量是哪几个。所以:va_start(va,dim);这里的dim是你输入的维度大小,是一个确定的参数。这里可能会有些不理解,等会我解释的,这里就先这样理解一哈。

  3. 刚刚把多个变量存到了va_list中,那么现在是不是要把它读取出来了。所以:ElementType i=va_arg(ap,ElementType);这里的ElemeentType是你自己变量的类型。通常这条语句是要放到一个for中去的,因为你变量的个数多半是多个。

  4. 最后,当你读取完毕后,作为一个有一个好的编程习惯,受过良好训练(怎么有点像狗)的学生来说,此时应该结束读取,所以: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:

  • va_list其实就是一个char的指针,和printf函数一样,都使用的是char.
  • #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )。可以看到va_start(ap,v)就是一个计算地址的过程。注意,这里的v一定是最后一个。其实一开始我也不知道,我也是无意间没有用最后一个参数,然后就报错了。(这里说明了,不要一昧的去模仿书上的代码,因为书上的代码基本上都对,而一些错误它也没有提醒,你自然也就不会去重视了
  • _INTSIZEOF(v)是什么捏,就是计算v的字节数,那为什么不去使用sizeof(v)捏。我们看到源码的最后面, #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) );也就是说, _INTSIZEOF(v)还是在用sizeof来计算字节数大小。看来别人大佬的文章,说就是为了内存对齐。至于为什么要内存对齐,可能就要需要操作系统之类的知识了(现在的我还没有),还有其实在sizeof(struct XXX)时就有内存对齐这一问题,同理,在c++中new XXX也是有的。这里就不多讲,反正内存对齐后,cpu读取速度就会变快,所以这里就没有单纯的使用sizeof了。
  • #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )。我刚刚看到的时候也有点迷。不知道他是怎么在计算的。首先,想弄清楚这一点,你的有一点c或c++内存四区的概念。我在这里就给大家看看是怎样计算的。这个就是怎么计算出可变变量地址的示意图,大家把ap += _INTSIZEOF(t)) - _INTSIZEOF(t)带入就好

好了,写了这么多,其实还是有一些底层是我现在弄不清楚的。所以捏,我等到学到了更多的底层知识再来补充这个文章。也欢迎大家在下方留言,我们一起讨论!

这是数据结构中实现多维数组的代码,注释都是我自己的理解,大家可以看看:是在vs2017下运行的,可能在你的编译器上没有pch.h文件,那就直接去掉它即可。如果运行后有一个nullptr无法识别的问题,就将nullptr改成NULL就好了。
这个是main.cpp

#include "pch.h"
#include"statement.h"
#include<stdio.h>

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;
}
</kbd>
这个是statement.h
<kbd>
#pragma once
#ifndef STATEMENT_H
#define STATEMENT_H

#include<stdarg.h>
#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<stdio.h>
#include<stdlib.h>
#include<stdarg.h>

//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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值