《C++ primer》英文第五版阅读笔记(十二)——数组

Chapter3,Section3.5 Arrays


数组


数组是一种和库类型vector类似的数据结构。但是具有不同的性能和灵活性。每个数组只能存储一种类型的对象,这些对象都是未命名的,我们可以通过位置来访问这些对象。数组有固定的大小,我们不能给一个数组添加元素。由于数组有固定的大小,它们有时具有很好的性能,但是灵活性可能不是很好。


如果不确定具体需要多少个元素时,可以使用vector。


(一)定义和初始化内置的数组

数组是一个复合类型。数组声明的形式为a[d]。a是数组名,d是数组的容量。d必须大于0。

数组元素的个数也是数组类型的一部分。因此在编译阶段必须知道数组的容量,也就是活数组的容量是一个常数表达式。


默认情况下,数组里面的元素是用默认值初始化的。

和内置类型变量相同,在函数内采用默认初始化方式定义的数组的值是undefined。


得到当定义了一个数组时,一定要指定数组的类型,不能用auto自动判别由初始化列表初始化的数组的类型。和vector一样,数组存储对象,因此没有引用类型的数组(例:int &refs[10] = /* ? */ ;出错)。



可以用初始化列表对数组进行初始化,此时可以省略数组的容量d。

如果省略了d,则数组的容量与初始化列表里面值的个数相同。

如果我们定义了d,那么初始化列表里面值的个数必须不能超过d。如果大于,则出错。如果小于,则用初始化列表里面有的值对数组进行初始化,剩余的值采用默认值初始化。


字符数组的初始化形式有些不同。

我们可以使用string literals对其进行初始化,当用这种形式时,要记住在string literals的末尾有一个空字符。那个空字符也会像字符一样复制进数组里面。


不能用一个数组去初始化另一个数组,把一个数组赋值给另一个数组也是非法的。

有些编译器允许数组赋值,它们将这个作为编译器的一种扩展。通常最好避免使用不在标准里面的特性。否则这样的程序可能在其他的编译器下不会工作。


复杂的数组声明

和vector一样,数组能够存储大多数类型的对象。比如,我们定义一个存储指针的数组。

因为每个数组中的元素都是一个对象,我们可以定义数组元素的指针和引用。定义一个存储指针的数组很容易,但是定义一个指针或引用指向数组有些复杂。

int *ptrs[10];//ptrs是一个存储10个整型指针元素的数组。

int (*Parray) [10] = &arr;//Parray是一个指针,指向含有10个元素的整型数组。

int (&Parray) [10] = arr;//Parray是一个引用,引用的是含有10个元素的整型数组

当数组的声明里面很复杂时,采用的读取顺序是“从里到外,从右到左”。


(二)使用数组中的元素


与vector和string类型一样,可以通过下标和range for来访问数组中的元素。下标从0开始。

数组的下标值的类型是size_t,它是特殊的机器无符号类型,它能够保证存储内存中任意大小的对象。它定义在cstddef头文件中,这是从C语言继承过来的头文件。


当给数组用空的{}进行初始化时,数组中的元素都采用默认值。

当使用数组时,一定要确定数组下标是在正确的范围之内的(大于0小于数组元素个数)。如果下标在范围之外,那么就有可能产生缓冲区溢出的问题。这种问题通常发生在编译器无法检查一个下标的合法性或者错误地使用了数组或相似的数据结构的正确范围之外的内存空间。


(三)指针和数组

当我们使用数组时,编译器会自动地将数组转换为指针。在大多数使用数组的表达式里面,我们真实使用的是指向那个数组首元素的指针。


如果想让一个指针指向数组元素,就用地址符对那个数组元素进行取地址操作,然后将值赋给指针。


当使用decltype时,由decltype(数组名)返回的将不是一个指针。而只是一个数组。


指向数组元素的指针支持迭代器在vector和string上的相同的操作。同样,我们不能在“指向数组末尾元素的下一个位置处”进行解引用和自增操作。


尽管我们能够对数组的末尾后一个元素进行计算,但是这样是容易出错的。

新标准里面定义了两个函数:begin和end来更容易地使用指针。它们定义在iterator头文件里面。

begin返回指向数组首元素的指针,end返回指向数组最后一个元素的下一个位置的指针。


指向数组元素的指针能够使用之前写到的迭代器的所有操作,并且操作的含义和迭代器的操作含义相同。

当指针与整数进行加减运算时,返回的是一个指针。返回的指针指向原指针的向前(加)或向后(减)的元素。加减的结果指针一定是指向同一数组中的元素,或是指向数组最后一个元素后面的位置。否则就会出错,但是编译器有时察觉不到这种错误。


两个指向同一个数组的指针相减,返回的是它们之间的元素个数。结果是ptrdiff_t类型的,它也是特殊机器类型,但是是一个有符号类型,定义在cstddef头文件里面。


指向同一个数组的指针可以进行比较大小,但是指向不同对象的指针不能比较大小。

指针的算术运算对于空指针和非数组对象是无效的。在今后的情况里,指针必须指向相同的对象,或者是对象的后一个位置。如果p是一个空指针,我们能把它和一个值在0到p范围内的整型常数表达式相加减。两个空指针也可以相减,结果是0。


指针的算术运算结果也可以进行解引用操作。


库类型里面的下标(比如vector和string)必须是无符号的,但是内置的下标可以是有符号的。当然结果指针一定是指向同一数组中的元素,或是指向数组最后一个元素后面的位置。


(四)C风格的字符串

C++里面的字符串literals是从C里面继承来的。C风格的strings并不是一个类型。它们是一种如何表示和使用字符strings的约定。遵守这种约定strings存储在字符数组里面,末尾有一个空的终止符('\0')。通常我们使用指针去使用

这些strings。


标准的C库里面提供了一些方法,它们用来操作C风格的字符串,定义在cstring头文件里面。列举如下:

这些方法并不对字符串参数进行检查。

strlen(p); 返回字符串长度,不包括'\0'

strcmp(p1,p2);比较字符串大小。=0表示p1=p2,>0表示p1>p2,<0表示p1<p2

strcat(p1,p2);字符串链接。如果目标字符串的大小估计错误,可能导致安全漏洞。

strcpy(p1,p2);字符串拷贝。

为了安全考虑,最好使用c++的string而不是C里面的。


(五)旧风格代码

C++库里提供了管理新旧风格代码的特性。

1.可以使用以'\0'结尾的字符数组去初始化或者给一个string赋值

2.可以在+的一侧使用以'\0'结尾的字符数组,让它和string相加,或者把以'\0'结尾的字符数组当做+=操作符的右操作数。

但是反过来就不行了。也就是说,当一个以C风格的string有需求的时候,不能使用库string。比如:不能用string去初始化一个字符指针。但是有一个string的成员函数c_str却可以做到。这个函数返回的是C风格的string。它返回一个以'\0'结尾的字符数组的开始,这个以'\0'结尾的字符数组含有和string相同的字符数据。但是这个函数返回的数组却不一定是有效的。任何可能改变源string值的操作都可能使返回的数组无效。


如果一个程序要继续使用c_str()返回的数组里面的内容,那么必须在程序中对c_str返回的数组进行复制。


我们不能用一个数组去初始化另一个数组,也不能用vector去初始化一个数组,但是可以用数组对vector进行初始化。这样做的话,我们要用到需要复制的元素的首地址和末地址(最后一个元素的下一个位置)。


(六)多维数组

1.多为数组初始化时可以使用嵌套的{},也可以将内层{}省略,还可以只对数组每行的第一个元素初始化。

2.处理多维数组时,常使用嵌套的for循环。也可以使用range for。在使用range for的时候,所有的循环控制变量除了最内层数组的,都要是引用类型的。

3.当定义了一个指向多维数组的指针时,要记住多维数组就是数组的数组。

4.在新标准里面,我们通常通过auto或者decltype来获得指向数组的指针的类型。

5.使用类型的别名简化了多维数组的指针。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值