文档章节

第三章 数组的解剖学

北极心
 北极心
发布于 2016/08/11 10:18
字数 1667
阅读 8
收藏 0

 

C/C++的数组不同于VB等语言的数组,是有层次的,这个层次指的不是维度,而是象俄罗斯有名的套娃一样,一维套一维,亦即数组的嵌套,数组的元素也是数组,VB等语言的数组与之相比更像一个平面。

 

数组嵌套这个现象从其它语言的角度来看有点奇特,但其实原因也很简单。C/C++的对象模型并不视数组为某种数值的简单集合,而是对象的聚集,每个元素都是一个对象。元素为整数对象,就是整数数组,为浮点数对象,就是浮点数数组。然而,数组本身也是一种对象,因此一个数组也能作为另一个数组的元素。当某个一维数组以一维数组作为元素时,这个一维数组每个元素都具有数组类型,这个一维数组其实是二维数组,同理,一个以二维数组作为元素的一维数组其实是三维数组。因此,使用C/C++数组的时候应该用数组嵌套的观点去看待。有人据此认为,C/C++的数组不是真正的数组,还有的认为C/C++没有多维数组,这些观点都有失偏颇,与其它语言的数组相比,两者只是同一事物的不同实例,是实现方法的不同,而本质是一样的,C/C++的数组嵌套可视为对数组概念的发展。

 

现在来看看数组的定义:

 

6.5.4.2 Array declarators

 

Semantics

 

If, in the declaration “T Dl.” Dl has the form

D [ constant expressionopt ]

 

这个定义非常简单,其中T代表元素类型,D代表标识符,constant expression必须为大于0的常量表达式,opt表示可选,即[]中的内容可以为空,当[]为空时叫不完整类型,表示这个数组对象的长度未知,不完整数组类型可以在程序的某个地方补充完整。细心的人马上就会发现,从形式上看,怎么只有一维数组的定义?这个形式如何定义多维数组?刚才说过,C/C++的数组是数组的嵌套,因此多维数组的定义也反映了这个本质。多维数组的定义是通过嵌套的一维数组定义构造的。对于一维数组:

 

T D[M]

 

当元素为一维数组T[N]时,元素的类型也为数组类型,用T[N]代替T,则为:

 

T[N] D[M]

 

这个语法结构不符合C/C++数组定义的语法形式,将[N]移动到[M]后,就是正式的二维数组的定义了:

 

T D[M][N]

 

其中D[0]---D[M-1]都是一维数组,具有数组类型T[N]。各种维度的多维数组可以用同样的嵌套方法构造出来。

 

一个一维数组T[M],经过数组到指针的转换后,类型转换为T*,二维数组T[M][N]转换为指针后,类型转换为T ( * )[N],有些初学者对T( * )[N]这种形式较难理解,怎么多了一维,形式就有这么大的差别呢,其实原理还是跟嵌套有关,二维数组为一维数组的嵌套,元素为数组类型,因此用T[N]代替T,则二维数组转换之后的指针类型为T[N] *,将[N]移动到*的右边,就是T*[N],由于[]的优先级比*高,因此需要加括号,就成为T( * )[N]了,否则就不是指针类型,而成了指针数组类型了。

 

围绕数组名,存在一些有趣的表达式,下面的内容通过讨论这些表达式中较为重要的几个,来加深对数组的理解。对于二维数组:

 

T a[M][N]

 

a 表达式中的a的类型转换为T ( * )[N],代表数组首元素的地址

&a:是一个指向二维数组对象的指针,类型为T ( * )[M][N]。在C标准出现之前,一些早期的实现并不允许&a,因为这些编译器认为此时的a转换为一个右值,而&运算符要求一个左值,因此非法。C标准委员会鉴于对象的概念已经得到了扩展,而且允许&a并没有害处,因此把&运算符作为一个例外写进了数组到指针的转换条款中。这种情况下的a代表数组对象,&a表示对数组对象取地址,因此&a的结果跟a是相同的,但类型不同。

              笔者曾经见过某些观点认为,&a才是数组的首地址,不是a。这个观点初看起来似乎很有道理,一个数组对象的引用,不正是首地址吗?但实际上这种论述是不符合标准的,数组到指针的转换条款规定,当产生一个points to the initial element of the array object的时候,其前提是由array of typepointer to type的转换,但&a的类型属于pointer to array of type,不是pointer to type,因此真正代表数组首地址的是a本身,不是&a

      (这几天关于这个问题又有了一些争论,好吧,就让这个问题的争吵到这里结束吧。本着尊重标准和去除先入为主偏见的原则,a就是数组首元素的地址,不是数组的首地址---2011年3月27日)

&a[0][0]:这是数组首元素的地址。&a[0][0]常常被误解为数组a的首地址,其实a[0][0]只不过由于位置特殊,其地址值才与a相同,&a[0][0]是一个T类型对象的引用,不是一个数组对象的引用,而且其类型不是由array of type转换得来的,因此其意义不是数组首地址。

a[i](其中 i >= 0 && i < M):从数组嵌套的观点来看,a是一个一维数组,元素的类型为数组类型,因此a[i]的类型为T[N],在表达式中转换为T*,是第i个一维数组的首地址。

a + 1a隐式转换为指针类型T( * )[N]然后加1,请记住指针加法是以指针指向对象的大小为步长的,因此a + 1将跨过N * sizeof( T )个字节。

&a + 1:与a + 1同理,&a类型为T( * )[M][N],因此&a + 1的步长为M * N * sizeof( T )

 

本文转载自:http://blog.csdn.net/code_crash/article/details/4855010

共有 人打赏支持
北极心
粉丝 35
博文 39
码字总数 16464
作品 0
深圳
后端工程师
私信 提问
13篇文章,教你学会ES6知识点

ES6 深入理解ES6》学习笔记 本文用于汇总链接到各个子章节的内容,github 欢迎大家题issues和PR,如果对你有帮助,也可以给 star 支持 :) 目录 第一章 块级绑定 第二章 字符串和正则表达式 ...

你听___
05/08
0
0
Modern Fortran Explained学习笔记4

第三章.表达式和赋值语句 今天继续从第三章开始学习,第三章主要讲了表达式和赋值语句,其中最主要的是数组的表达和指针等概念的使用。 需要特别注意的点其实不多,主要的东西都是自动实现的...

粗衣大布裹天涯
2017/10/30
0
0
第六章、epub文件处理 -- 解析container文件与.opf文件

第六章、epub文件处理 -- 解析container文件与.opf文件 这一章我们会接着第三章结尾介绍的FBReaderApp类的openBookInternal继续,开始介绍解析container文件与.opf文件。 这一章中会涉及到第...

charlessun
2014/10/21
0
0
JavaScript 入门教程--WisdomPlanet-Javascript-Primer

WisdomPlanet-Javascript-Primer 是智慧星球 Javascript 入门教程。 本教程写给:正准备踏入编程之路或对 Javascript 感兴趣的同学 作者 念念之间 版权声明 本文允许您用于非商业用途,若有商...

念念之间
2015/04/01
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Eos如何删除钱包

在使用Eos的keosd钱包软件时,如果要删除EOS中指定名称的钱包,最简单的办法是直接删除钱包文件,不过在删除钱包之前,需要先停止钱包软件的运行。 学习EOS应用开发要选这个:【EOS智能合约与...

geek12345
4分钟前
0
0
js操作时间

获取当前时间 function getSystemDate(){ var systemDate = new Date(); // 获取当年 var year = systemDate.getFullYear(); // 获取当月 (月+1是因为js中......

简心
11分钟前
0
0
区块链开发教程推荐

区块链的重要性已经毋庸置疑,但对大多数跃跃欲试的开发者而言,去中心化思想、非对称加密、共识算法等技术点的理解和运用,都是入门区块链开发的挑战。合适的区块链开发教程可以极大地缩短区...

笔阁
11分钟前
0
0
菜单menuView总结

1、FTPopOverMenu

_____1____
22分钟前
2
0
MyEclipse教程:Web开发——部署和测试Web项目

MyEclipse 在线订购年终抄底促销!火爆开抢>> MyEclipse最新版下载 本教程向用户展示了使用关联的Web项目创建Web片段项目的机制。用户还可以获得要检查的示例项目。在本教程中,用户将学习如...

电池盒
38分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部