频道栏目
读书频道 > 软件开发 > C++ > C++ Primer中文版(第5版)
3.5.1 定义和初始化内置数组
2013-09-09 09:52:04     我来说两句
收藏   我要投稿

本文所属图书 > C++ Primer中文版(第5版)

这本久负盛名的 C++经典教程,时隔八年之久,终迎来史无前例的重大升级。除令全球无数程序员从中受益,甚至为之迷醉的——C++ 大师 Stanley B. Lippman 的丰富实践经验,C++标准委员会原负责人 Jos&eacut...  立即去当当网订购

数组是一种复合类型(参见2.3节,第50页)。数组的声明形如a[d],其中a是数组的名字,d是数组的维度。维度说明了数组中元素的个数,因此必须大于0。数组中元素的个数也属于数组类型的一部分,编译的时候维度应该是已知的。也就是说,维度必须是一个常量表达式(参见2.4.4节,第65页):

unsigned cnt = 42;           // 不是常量表达式

constexpr unsigned sz = 42; // 常量表达式,关于constexpr,参见2.4.4节(第66页)

int arr[10];                 // 含有10个整数的数组

int *parr[sz];               // 含有42个整型指针的数组

string bad[cnt];                 // 错误:cnt不是常量表达式

string strs[get_size()];         // 当get_size是constexpr时正确;否则错误

默认情况下,数组的元素被默认初始化(参见2.2.1节,第43页)。

和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。

定义数组的时候必须指定数组的类型,不允许用auto关键字由初始值的列表推断类型。另外和vector一样,数组的元素应为对象,因此不存在引用的数组。

显式初始化数组元素

可以对数组的元素进行列表初始化(参见3.3.1节,第98页),此时允许忽略数组的维度。如果在声明时没有指明维度,编译器会根据初始值的数量计算并推测出来;相反,如果指明了维度,那么初始值的总数量不应该超出指定的大小。如果维度比提供的初始值数量大,则用提供的初始值初始化靠前的元素,剩下的元素被初始化成默认值(参见3.3.1节,第98页):

const unsigned sz = 3;

int ia1[sz] = {0, 1, 2};        // 含有3个元素的数组,元素值分别是0, 1, 2

int a2[] = {0, 1, 2};           // 维度是3的数组

int a3[5] = {0, 1, 2};          //等价于a3[] = {0, 1, 2, 0, 0}

string a4[3] = {"hi", "bye"};   // 等价于 a4[] = {"hi", "bye", ""}

int a5[2] = {0,1,2};            // 错误:初始值过多

字符数组的特殊性

字符数组有一种额外的初始化形式,我们可以用字符串字面值(参见2.1.3节,第39页)对此类数组初始化。当使用这种方式时,一定要注意字符串字面值的结尾处还有一个空字符,这个空字符也会像字符串的其他字符一样被拷贝到字符数组中去:

char a1[] = {’C’, ’+’, ’+’};        // 列表初始化,没有空字符

char a2[] = {’C’, ’+’, ’+’, ’\0’}; // 列表初始化,含有显式的空字符

char a3[] = "C++";                  // 自动添加表示字符串结束的空字符

const char a4[6] = "Daniel";        // 错误:没有空间可存放空字符!

a1的维度是3,a2和a3的维度都是4,a4的定义是错误的。尽管字符串字面值"Daniel"看起来只有6个字符,但是数组的大小必须至少是7,其中6个位置存放字面值的内容,另外1个存放结尾处的空字符。

不允许拷贝和赋值
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值:

int a[] = {0, 1, 2};    // 含有3个整数的数组

int a2[] = a;           // 错误:不允许使用一个数组初始化另一个数组

a2 = a;                 // 错误:不能把一个数组直接赋值给另一个数组

一些编译器支持数组的赋值,这就是所谓的编译器扩展(compiler extension)。但一般来说,最好避免使用非标准特性,因为含有非标准特性的程序很可能在其他编译器上无法正常工作。

理解复杂的数组声明
和vector一样,数组能存放大多数类型的对象。例如,可以定义一个存放指针的数组。又因为数组本身就是对象,所以允许定义数组的指针及数组的引用。在这几种情况中,定义存放指针的数组比较简单和直接,但是定义数组的指针或数组的引用就稍微复杂一点了:

int *ptrs[10];              // ptrs是含有10个整型指针的数组

int &refs[10] = /* ? */;         // 错误:不存在引用的数组

int (*Parray)[10] = &arr;   // Parray指向一个含有10个整数的数组

int (&arrRef)[10] = arr;    // arrRef引用一个含有10个整数的数组

默认情况下,类型修饰符从右向左依次绑定。对于ptrs来说,从右向左(参见2.3.3节,第58页)理解其含义比较简单:首先知道我们定义的是一个大小为10的数组,它的名字是ptrs,然后知道数组中存放的是指向int的指针。

但是对于Parray来说,从右向左理解就不太合理了。因为数组的维度是紧跟着被声明的名字的,所以就数组而言,由内向外阅读要比从右向左好多了。由内向外的顺序可帮助我们更好地理解Parray的含义:首先是圆括号括起来的部分,*Parray意味着Parray是个指针,接下来观察右边,可知道Parray是个指向大小为10的数组的指针,最后观察左边,知道数组中的元素是int。这样最终的含义就明白无误了,Parray是一个指针,它指向一个int数组,数组中包含10个元素。同理,(&arrRef)表示arrRef是一个引用,它引用的对象是一个大小为10的数组,数组中元素的类型是int。

当然,对修饰符的数量并没有特殊限制:

int *(&arry)[10] = ptrs; // arry是数组的引用,该数组含有10个指针

按照由内向外的顺序阅读上述语句,首先知道arry是一个引用,然后观察右边知道,arry引用的对象是一个大小为10的数组,最后观察左边知道,数组的元素类型是指向int的指针。这样,arry就是一个含有10个int型指针的数组的引用。

要想理解数组声明的含义,最好的办法是从数组的名字开始按照由内向外的顺序阅读。

您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:3.5 数组
下一篇:3.5.1节练习
相关文章
图文推荐
排行
热门
最新书评
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站