1. 写在前面的

在前文中,我主要介绍了数组的一些相关知识,希望加深各位对Array的理解,不过,看过Ivony…同学的回复,我发觉自己离说透还有很大的距离,于是就有了下面的文章。在本文中,我也主要来围绕Ivony…同学提出的几点问题来作以说明,问题如下:

A、数组在托管堆内部是怎么存放的?数组元素的位置是连续的么?
B、非零基数组可以和零基数组转换么?
C、int[]与System.Array的关系到底是同一类型?还是基类与派生类的关系?
D、ldelem不检查下标越界么?
E、多维数组可以和零基数组转换么?
F、Array.Copy和CopyTo与手动拷贝性能有多大差距?
G、数组的协变是怎么做到的?
H、数组是如何实现泛型接口(如IList<T>)的?
I、多维数组每一维度长度必须相等么?必须零基么?
J、数组的Length属性到底指示的是什么?

2. 数组内存详解

在这里,我们依然把数组分为零基数组和非零基数组来讨论。

首先来看零基数组的内存分配,废话少说,我们先来看测试代码:

static unsafe void Main(string[] args)
{int[] intArr = new int[3];intArr[0] = 1;intArr[1] = 2;intArr[2] = 3;
}

代码本身很简单,接下来单步执行向下看,首先我们来查看一下源代码的汇编代码:

            int[] intArr = new int[3];
00000035  mov         edx,3
0000003a  mov         ecx,61CD4192h
0000003f  call        FFFB2140
00000044  mov         dword ptr [ebp-44h],eax
00000047  mov         eax,dword ptr [ebp-44h]
0000004a  mov         dword ptr [ebp-40h],eax intArr[0] = 1;
0000004d  mov         eax,dword ptr [ebp-40h]
00000050  cmp         dword ptr [eax+4],0
00000054  ja          0000005B
00000056  call        624B6B29
0000005b  mov         dword ptr [eax+8],1 intArr[1] = 2;
00000062  mov         eax,dword ptr [ebp-40h]
00000065  cmp         dword ptr [eax+4],1
00000069  ja          00000070
0000006b  call        624B6B29
00000070  mov         dword ptr [eax+0Ch],2 intArr[2] = 3;
00000077  mov         eax,dword ptr [ebp-40h]
0000007a  cmp         dword ptr [eax+4],2
0000007e  ja          00000085
00000080  call        624B6B29
00000085  mov         dword ptr [eax+10h],3 }

在这里,我们就可以清晰地发现,在0x0000005b,0x00000070和0x00000085中,mov操作的目标地址之间是相隔4个Bytes的,也就是一个整数位。接下来我们来进一步证实。

当我们为数组分配过内存地址后,打开即使窗口查看数组所在的内存地址。

接下来打开内存窗口还查看0x015cc790内存块的数据:

以上是对数组赋值前的情况,赋值后的内存数据如下:

在这里可以更清晰地看出,数组元素之间差的正好是4个Bytes,也就是一个整数位。由此,我们可以得出结论。零基数组的元素在内存中是连续排布的。

接下来我们来看一下非零基数组:

由于空间所限,过程如上,就不再发,截图证明:

总之,当我们在托管堆中为数组分配内存时,数组占据一段连续的内存空间。

我们知道,当我们在托管堆中初始化一个对象时,每个对象都需要维护一个指针,该指针的作用是指向下一块空闲内存空间,由于对数组的操作经常是循环遍历等操作,这样如果把数组分配到一个连续的内存空间有一下两个好处:

A. 减少内存碎片

B. 节省内存,不需要维护指针

C. 基地址不需要发生变化,只需要改变偏移量即可,在一定程度上也提高了访问的效率。

接下来,我们还需要来补充一下数组在栈上分配内存的情况:

还记得上文中提到的这个关键字吧,stackalloc,就是他了。补充一下,在上文的回复中,有人问到说栈空间上分配的内存是不是也被垃圾回收器回收?这里的栈空间和C语言中的栈一样,没有垃圾回收器,每个变量都有他自己的作用域,当出了作用域后,变量自动销毁,具体的函数执行过程,请参看《深入理解计算机系统》。

3. 再论零基数组和非零基数组

我们先来看这样一段代码:

static void Main(string[] args)
{int[,] intArr = (int[,])(Array.CreateInstance(typeof(Int32), new int[] { 3,4 }, new int[] { 1,1 }));intArr[2, 3] = 1;
}

这段代码没有问题,我们将Array显式地转换成了强类型的二维数组,然后直接访问索引对其复制。

但是我们知道,对弱类型的Array而言,我们不能通过其下标访问他的元素,而只能通过SetValue和GetValue来获得值,但是我们看到SetValue和GetValue访问和设置的值的类型都是Object,这就意味着我们需要对其进行一次装箱或者拆箱。那么我们有没有办法也生成一个强类型的非零基数组呢?

在上文中,我们提到过,.NET Framework的几种数组类型:

一维零基数组:System.Int32[]。一维非零基数组:System.Int32[*]。多维数组:System.Int32[,]。

那么也就是说,我们是否能通过这样的代码来把Array转换成一维非零基数组呢?

static void Main(string[] args)
{int[*] intArr = (int[*])(Array.CreateInstance(typeof(Int32), new int[] { 3}, new int[] { 1 }));
}

事实证明是错误的。在CLR via C#中Jeffery有这样一段话:

“C# does not allow you to declare a variable of type string[*],and therefore it is not possible to user C# syntax to access a single-dimensional ,non-zero-based array.”

这段话翻译成中文的意思就是:C#不允许声明一个string[*]类型的变量,因此,我们能够使用C#语法来访问一个非零基一维数组。

通过以上的解释,我们也许又额外明白了一点,Array究竟是数组类型,还是数组类型的基类?

通过上面的一些代码,我们不妨又把数组重新分类为“强类型数组”和“弱类型数组”。而Array就属于弱类型数组。为什么Array是所有数组类型的基类,我没想出办法来如何证明,只是看到Jeffery说了这样一句话:

“All Arrays are Implicitly Derived from System.Array”。

我想这句话可以说明问题了,不过还是希望各位大侠指点如果证明这一点。

未完持续…………

转载于:https://www.cnblogs.com/kym/archive/2009/10/09/1579958.html

把Array说透(续一)相关推荐

  1. 老男孩上海校区Python面试题

    python面试题 第一章:python基础 数据类型: 1 字典: 1.1 现有字典 dict={'a':24,'g':52,'i':12,'k':33}请按字典中的 value 值进行排序? 1. ...

  2. atl offsetofclass

    首页 | 互联网 | IT动态 | IT培训 | Cisco | Windows | Linux | Java | .Net | Oracle | 软件测试 | C/C++ | 嵌入式 | 存储世界服 ...

  3. iPhone13下周三发布,提前看剧透:刘海缩小、120Hz高刷屏、Mini又续一年…

    梦晨 发自 凹非寺 量子位 报道 | 公众号 QbitAI 不少人在期盼着iPhone13,毕竟是"十三香". 到目前为止,苹果的动作和网上的"剧透"那是一模一 ...

  4. 十二之续、快速排序算法的深入分析

    十二之续.快速排序算法的深入分析 作者:July   二零一一年二月二十七日 -------------------------- 前言 一.快速排序最初的版本 二.Hoare版本的具体分析 三.Ho ...

  5. java array 元素的位置_Java常见面试题 非常实用「个人经验」

    Java 容器都有哪些 Collection 的子类 List.Set List 的子类 ArrayList.LinkedList等 Set 的子类 HashSet.TreeSet等 Map 的子类 ...

  6. 面向对象程序设计_面向对象的程序设计(续)

    写在前面:这里是续之前的那篇文章,同样源于本人的个人博客,在知乎为了一个备份 这里是7-10章 Chapter 7: Arrays 数组 记得在C语言里面,也有提到过数组这个概念 记得RT也说过这玩意 ...

  7. 《编程珠玑(续)(修订版)》—第2章2.1节Awk中的关联数组

    本节书摘来自异步社区<编程珠玑(续)(修订版)>一书中的第2章,第2.1节Awk中的关联数组,作者[美]Jon Bentley,更多章节内容可以访问云栖社区"异步社区" ...

  8. js array 删除指定元素_Array 原型方法源码实现解密

    作者:木易杨 引言 今天这篇文章主要看看 ECMA-262 规范中是如何定义这些方法的,并且在看完规范后我们用 JS 模拟实现下,透过源码探索一些底层的知识,希望本文对你有所帮助. Array.pro ...

  9. 通过脚本下派WsusAgent3.0.exe(续)

    在http://yangye.blog.51cto.com/922715/200444这篇文章中我提到过使用脚本结合调用runas命令来给客户端安装exe文件.如果做过测试的人会发现,这个脚本并不是很 ...

最新文章

  1. 规格表管理之删除规格表数据
  2. R语言jitter函数为数据添加噪声(noise)扰动信息实战
  3. Android Linux自带iptables配置IP访问规则
  4. linux开发常用脚本,记录自己常用的一些 Linux Shell 脚本
  5. CVPR 2018 MCCT:《Multi-Cue Correlation Filters for Roubust Visual Tracking》论文笔记
  6. centos 卸载自带的 java
  7. IUnknown接口QueryInterface函数介绍
  8. c iostream.源码_通达信《K线上画趋势线预警》精选指标(附源码)
  9. 表单提交数据丢失的问题
  10. php exec执行多条命令,小技巧:在PHP中调用多条shell指令
  11. 我要做 Android 之面笔试
  12. 机器人总动员中的小草_机器人总动员观后感(精选4篇)
  13. 交通灯控制系统的设计
  14. Unity 粒子特效 之 LogoEffect ParticleSystem 文字图片logo粒子特效
  15. 【空气质量数据分析专题二】数据获取及预处理
  16. 用EXCEL宏编写坐标转换
  17. 2021年低压电工及低压电工证考试
  18. 【android学习之十六】——特色功能1:GoogleMap手机地图
  19. 业务元数据管理——洞悉数据背后的业务含义
  20. 腾讯财报:2018年Q3腾讯净利润197.1亿元 同比增长15%

热门文章

  1. DC学院爬虫学习笔记(六):浏览器抓包及headers设置
  2. 远程管理客户端--SCCM
  3. 我来了 开源社区的兄弟门
  4. 如何验证自己的网络是否支持ipv6
  5. Dubbo快速启动示例
  6. mysql str_to_date 字符串转换为日期
  7. 重新理解 Monad
  8. 从智能客服说起,看小i机器人如何用AI赋能产业升级改造|M-TECH AI助力中国智造产业论坛...
  9. 云安全趋势下脚踏实地力拼网络危胁
  10. IEEE Spectrum 2014 年度编程语言排名