设为首页 加入收藏

TOP

3.5.1 定义和初始化内置数组
2013-10-07 16:28:28 来源: 作者: 【 】 浏览:104
Tags:3.5.1 义和 初始 内置

3.5.1  定义和初始化内置数组

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

  1. unsigned cnt = 42;          // 不是常量表达式  
  2. constexpr unsigned sz = 42; // 常量表达式,关于constexpr,参见2.4.4节(第66页)  
  3. int arr[10];                // 含有10个整数的数组  
  4. int *parr[sz];              // 含有42个整型指针的数组  
  5. string bad[cnt];                // 错误:cnt不是常量表达式  
  6. string strs[get_size()];        // 当get_size是constexpr时正确;否则错误 

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

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

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

显式初始化数组元素

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

  1. const unsigned sz = 3;  
  2. int ia1[sz] = {0, 1, 2};        // 含有3个元素的数组,元素值分别是0, 1, 2  
  3. int a2[] = {0, 1, 2};           // 维度是3的数组  
  4. int a3[5] = {0, 1, 2};          //等价于a3[] = {0, 1, 2, 0, 0}  
  5. string a4[3] = {"hi", "bye"};   // 等价于 a4[] = {"hi", "bye", ""}  
  6. int a5[2] = {0,1,2};            // 错误:初始值过多 

字符数组的特殊性

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

  1. char a1[] = {'C', '+', '+'};        // 列表初始化,没有空字符  
  2. char a2[] = {'C', '+', '+', '\0'};  // 列表初始化,含有显式的空字符  
  3. char a3[] = "C++(www.cppentry.com)";                  // 自动添加表示字符串结束的空字符  
  4. const char a4[6] = "Daniel";        // 错误:没有空间可存放空字符! 

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

不允许拷贝和赋值

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

  1. int a[] = {0, 1, 2};    // 含有3个整数的数组  
  2. int a2[] = a;           // 错误:不允许使用一个数组初始化另一个数组  
  3. aa2 = a;                     // 错误:不能把一个数组直接赋值给另一个数组 

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

理解复杂的数组声明

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

  1. int *ptrs[10];              // ptrs是含有10个整型指针的数组  
  2. int &refs[10] = /*   */;        // 错误:不存在引用的数组  
  3. int (*Parray)[10] = &arr;   // Parray指向一个含有10个整数的数组  
  4. 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。

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

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

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

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

3.5.1节练习

练习3.27:假设txt_size是一个无参数的函数,它的返回值是int。请回答下列哪个定义是非法的?为什么?

unsigned buf_size = 1024;

(a) int ia[buf_size];   (b) int ia[4 * 7 - 14];

(c) int ia[txt_size()];   (d) char st[11] = "fundamental";

练习3.28:下列数组中元素的值是什么?

  1. string sa[10];  
  2. int ia[10];  
  3. int main() {  
  4.      string     sa2[10];  
  5.      int        ia2[10];  

练习3.29:相比于vector来说,数组有哪些缺点,请列举一些。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇3.4.2 迭代器运算 下一篇3.5.2 访问数组元素

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: