几乎所有的语言都把数组作为一种固有的数据类型,数组也是我们最常用的数据结构之一。在语言底层,数组是如何实现的呢?本文以抽象数据类型的形式,定义、实现数组。

  创建数组,理论上,我们可以使用创建任意维度的数组;但这个多维只是我们“感知”上的多维度,实际上,内存是一种线性存储单元,不可能实现真正的多维。换言之,多维数组在内存中也是顺序的排在一维,占用连续的一段存储空间。

  以二维数组为例。存储数组时,可以使用行优先存储,即先存第一行…再存第二行……当然也可以使用列优先(Fortran语言就采用了列优先)。大多数语言还是行优先的,下面我就以行优先存储,定义和表示数组。


Def:n维数组的映像函数

一个数组的各个维度的下标与内存中的存储单元有着一一对应,这是必然的。这个对应关系称作映像函数。以三维为例,映像函数是:(这里就不展开一般情况了)

Loc(i,j,k) = Loc(0,0,0) + ((b2*b3)*i + (b3) * j + i) * L

其中:

Loc(0,0,0): 基地自

bi: 为第i维的长度 ;
        L : 地址的加减以元素大小为单位

为了方便程序,映像函数显然可以定义如下:

Loc(j1,j2,j3) = Loc(0,0,0) + sum(ci*ji)

其中:

i = 1 to n

cn = L;ci-1 = ci*bi

  可见,计算各元素位置的时间相同,存取任意元素时间都是单位O(1)。这叫做“随机存储结构” 。


  有了上述定义,就可以实现数组,以及实现数组的基本操作。下面的实现使用了“可变长参数”,后面我可给出了比较详细的参考资料。下面的注释非常清楚了。

 #include<cstdio>
#include<iostream>
#include<stdlib.h>
#include<stdarg.h> //标准头文件,提供宏va_start,va_arg,va_end。来使用可变长参数
#define MAX_DIM 8 //最大维度
#define ElemType int //数据类型
using namespace std;
//数组 表示
typedef struct{
ElemType *base; //数组的基地址
int dim; //维度
int *bounds; //维度是可变参数,存各维度的长度
int *constants; //这里就是上面说的ci
}Array; /*
基本操作如下
*/
void InitArray(Array &A,int dim,...){
/*先判断维度,以及各维度长度是否合法*/
if(dim<||dim >MAX_DIM) return;
A.dim = dim;
A.bounds = (int*)malloc(dim*sizeof(int)); //bounds存各维度的长度,故开辟空间为dim*dizeof(int)
if(!A.bounds) exit();
int elemtotal = ; //元素总个数,实际上,总个数为各维度长度的乘积
//先传一个数组,用来存可变长参数信息表的数组, dim是长度
va_list argp;
//等同于 char*argp,va_list就是一个指向第一个可变参数的指针
/*argp指向传入的第一个可选参数,第二个参数是可变参数之前的第一个参数名,必须有,因为要靠这个去找可变参数在哪里*/
/*这里把上面得到的字符指针argp,后移动4个字节,就是跳过dim的内存地址 */
va_start(argp,dim);
for(int i = ;i<dim;i++){
A.bounds[i] = va_arg(argp,int);//这里把ap往后跳过4个字节(sizeof(int)大小)指向下一个参数,返回的是当前参数(而非下一个参数)
if(A.bounds[i]<) return;
elemtotal *= A.bounds[i];
}
va_end(argp); A.base = (ElemType*)malloc(elemtotal * sizeof(ElemType));
if(!A.base) exit(); //下面再求映像函数的参数常数ci,有了这个可以很方便地取每一个元素
A.constants = (int*)malloc(dim*sizeof(int));
if(!A.constants) exit();
A.constants[dim-] = ;//L = 1,指针得增减以元素的大小为单位
for(int i = dim-;i>=;i--){
A.constants[i] = A.bounds[i+] * A.constants[i+];
}
return;
}
/*销毁函数*/
void Destory(Array &A){
if(!A.base){
return;
}
free(A.base);A.base = NULL;
if(!A.bounds){
return;
}
free(A.bounds);A.bounds = NULL;
if(!A.constants){
return;
}
free(A.constants);A.constants = NULL;
return;
}
/*求指定元素的地址,即off,求的是相对基地址的偏移*/
void Locate(Array A,va_list ap,int &off){
off = ;
for(int i = ;i<A.dim;i++){
//用va_arg去取元素,然后自动后移int位
//va_list ap就是第一个元素的地址
int ind = va_arg(ap,int);
if(ind<||ind>=A.bounds[i]) return; //是否合法
//求地址的公式!
off += ind * A.constants[i];
}
//return off;
} void Value(Array A,ElemType &e,...) {
//A是n维数组 e 是要去取得元素,...是n个下标 //创建取不定参数的指针
va_list ap;
//初始化,通过va_start() 实现
va_start(ap,e); int off;
//off存放元素e地址
Locate(A,ap,off);
//取元素,返回e
if(off <= ) return; //这其实也判断了下标是否合法
//关闭
va_end(ap);
e = *(A.base+off);
}
//赋值语句
void Assign(Array &A,ElemType e,...){
//A是n维数组,e是元素变量,...是下标
//即把指定下标的元素赋值成e
va_list ap;
va_start(ap,e);
int off;
Locate(A,ap,off);
if(off <= ) return;
*(A.base + off) = e;
va_end(ap);
return;
} int main(){
//测试 Array A;
InitArray(A,,,,);
Assign(A,,,,);
Assign(A,,,,);
int e = ;
Value(A,e,,,);
printf("e1:%d\n",e);
int e2 = ;
Value(A,e2,,,);
printf("e2:%d\n",e2);
printf("成功...");
}

上面用到的可变参数以及stdarg头文件的比较好的学习信息:

https://www.cnblogs.com/justinzhang/archive/2011/09/29/2195969.html

总结使用stdarg的步骤:

    va_start() va_arg() va_end() va_list 的使用:
<Step > 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step > 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
<Step > 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
<Step > 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。

【C/C++】C/C++中的数组是怎么实现的?的更多相关文章

  1. 前端开发:Javascript中的数组,常用方法解析

    前端开发:Javascript中的数组,常用方法解析 前言 Array是Javascript构成的一个重要的部分,它可以用来存储字符串.对象.函数.Number,它是非常强大的.因此深入了解Array ...

  2. JavaScript jQuery 中定义数组与操作及jquery数组操作

    首先给大家介绍javascript jquery中定义数组与操作的相关知识,具体内容如下所示: 1.认识数组 数组就是某类数据的集合,数据类型可以是整型.字符串.甚至是对象Javascript不支持多 ...

  3. java 在循环中删除数组元素

    在写代码中经常会遇到需要在数组循环中删除数组元素的情况,但删除会导致数组长度变化. package com.fortunedr.thirdReport; import java.util.ArrayL ...

  4. Objective-C中把数组中字典中的数据转换成URL

    可能上面的标题有些拗口,学过PHP的小伙伴们都知道,PHP中的数组的下标是允许我们自定义的,PHP中的数组确切的说就是键值对.而在OC我们要用字典(Dictionary)来存储,当然了Java用的是M ...

  5. GCC 中零长数组与变长数组

    前两天看程序,发现在某个函数中有下面这段程序: int n; //define a variable n int array[n]; //define an array with length n 在 ...

  6. C++中的数组

    数组名作为参数时,传递的是数组的首地址, 主调函数中实参数组元素个数不应该少于形参数组的元素个数 把数组名作为参数时,一般不指定数组第一维的大小 即使指定,编译时也会被忽略的.

  7. javascript中关于数组的一些鄙视题

    一.判断一个数组中是否有相同的元素 /* * 判断数组中是否有相同的元素的代码 */ // 方案一 function isRepeat1(arrs) { if(arrs.length > 0) ...

  8. Oc中的数组

    ========================== 数组 ========================== 一.认识数组 oc中可以把NSObject对象的子类放到数组这个集合中,但是int.f ...

  9. Javascript中判断数组的正确姿势

    在 Javascript 中,如何判断一个变量是否是数组? 最好的方式是用 ES5 提供的 Array.isArray() 方法(毕竟原生的才是最屌的): var a = [0, 1, 2]; con ...

  10. C#中的数组,多维数组和交错数组

    想研究一些面向对象的东西,也许是代码写得还不够多.感觉还不好,看那些教程,不是嫌太水就是太难看不懂.心情很是落寞 不过再怎样也要坚持每天发一篇博客. 这篇来说一下C#中的数组,多维数组,交错数组的一些 ...

随机推荐

  1. Luogu P3165 [CQOI2014]排序机械臂

    先讲一下和这题一起四倍经验的题: Luogu P4402 [Cerc2007]robotic sort 机械排序 SP2059 CERC07S - Robotic Sort UVA1402 Robot ...

  2. 深入理解Spring Boot数据源与连接池原理

    ​ Create by yster@foxmail.com 2018-8-2 一:开始 在使用Spring Boot数据源之前,我们一般会导入相关依赖.其中数据源核心依赖就是spring‐boot‐s ...

  3. springcloud(十三):Eureka 2.X 停止开发,但注册中心还有更多选择:Consul 使用详解

    在上个月我们知道 Eureka 2.X 遇到困难停止开发了,但其实对国内的用户影响甚小,一方面国内大都使用的是 Eureka 1.X 系列,另一方面 Spring Cloud 支持很多服务发现的软件, ...

  4. RabbitMQ 高可用之镜像队列

    如果RabbitMQ集群只有一个broker节点,那么该节点的失效将导致整个服务临时性的不可用,并且可能会导致message的丢失(尤其是在非持久化message存储于非持久化queue中的时候).可 ...

  5. React 系列 - 写出优雅的路由

    前言 自前端框架风靡以来,路由一词在前端的热度与日俱增,他是几乎所有前端框架的核心功能点.不同于后端,前端的路由往往需要表达更多的业务功能,例如与菜单耦合.与标题耦合.与"面包屑" ...

  6. Python-认识正则表达式-53

    # 计算器# re模块# 正则表达式 —— 字符串匹配的# 学习正则表达式# 学习使用re模块来操作正则表达式 #判断手机号是否符合要求 while True: phone_number = inpu ...

  7. 分解质因数FZU - 1075

    题目简述:就是给一个数,把他拆分成多个素数的乘积,这正好是算术基本定理.本题我的解决方法是埃氏素数筛+质因数保存...开始T掉了,是因为我在最后枚举了素数,保存他们的次数,然后两次for去查询他们的次 ...

  8. p57商环

    1.半群满足对乘法封闭吗? 2.理想I 又不是R的子群,为什么I是R的正规子群呢? 3.~为什么对加法是同余关系? 4. 属于R,b-b属于I,为什么R作用在I上面,还属于I呢? 1.封闭 2.理想I ...

  9. 使用ajax请求后端程序时,关于目标程序路径问题

    这里涉及到和PHP中类似的问题,有待更新!!!

  10. ocrosoft 程序设计提高期末复习问题M 递归求猴子吃桃

    http://acm.ocrosoft.com/problem.php?cid=1172&pid=12 题目描述 猴子吃桃问题.猴子第1天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个. ...