数组
背景
对一系列基础概念的整合。(对大学笔记的一个整理)
基本概念
数组可以存储相同类型的多个数据项。
实际上,只要有可用的内存,数组就可以存储任何类型的数据。
数组是表示一系列内存空间的变量,每个内存空间都可以存储相同类型的一个数据项。
数组的大小必须用常量整数表达式来指定。
编译器在编译时能够计算的任何整数表达式都可以用来指定数组的大小,不过最常用的是整数字面量,或是使用字面量初始化的const整型变量。
使用一个整数可以引用数组元素,把该整数称为数组的索引。
数组元素的索引是指该元素与数组中第一个元素的偏移值。
要引用元素,可以在数组名后面的方括号中放置其索引。
示例一
假如定义一个包含366个温度值的数组
1 | //Define an array of 366 temperatures |
示例二
要把temperatures数组的第四个元素设置成99.9
1 | //Set the fourth array element to 99.9 |
示例三
定义带初始值的数组
1 | //Define & initialize array of 6 heights |
示例四
常用的一些用法
1 | //Element values: 22 33 55 0 0 0 |
示例五
1 | unsigned int height[6] {22, 33, 55}; |
用初始化列表定义数组的大小
在数组的定义中提供一个或多个初始值,就可以忽略数组的大小。
元素的个数就是初始值的个数。
1 | int values[] {2, 3, 4, 5, 6, 7}; |
忽略数组大小的优点是,数组的大小不会出错,编译器会自动确定它。
确定数组的大小
有时候,需要一种确定数组大小的验证方法。
size_t类型
size_t并不是内置的基本类型名称,如int,float等;而是标准库定义的一个类型别名。
更具体的说,它是某个不带符号的整数类型的别名,并且足够大,能够容纳编译器支持的任何类型(包括数组)。别名在cstddef头文件以及其他一些头文件中定义。
但是,在实际应用中,大多数时候不需要使用#include来显式包含这些头文件,就能够使用size_t别名;通过包含其他更加高级的头文件(比如iostream头文件),常常就已经间接定义了这个别名。
sizeof运算符
sizeof运算符返回size_t的值,它是不带符号的整数类型,一般用于计算对象占用内存的字节数和数组的元素个数。
sizeof运算符返回变量占用的字节数,它适用于整个数组和单个数组元素。
因此sizeof运算符提供了确定数组中元素个数的方法:
只需要用数组的大小除以单个元素的大小即可。
1 | int values[] { 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
std::size()函数(c++17中新添加到标准库的)
如果实现支持,那么最简单也最推荐使用的方法是使用标准库的array头文件中提供的std::size()函数。
从技术上讲,std::size()主要是在iterator头文件中定义。但是,这是一个非常常用的函数,所以标准库保证了在包含array头文件(以及其他几个头文件)时,也可以使用std::size()。
因为我们主要是对数组使用std::size(),所以认为,记住包含array头文件比记住包含iterator头文件更容易。
std::size()函数不只用于数组,还可以用来获得标准库定义的任何元素集合的大小,包括std::vector<>和std::array<>容器。
1 | int values[] { 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
使用std::size()显然比sizeof表达式更容易理解。
因此,只要有可能,就应该总是使用std::size()。
注意
(1)数组的类型决定了存储每个数组元素所需的内存量。
数组的所有元素都存储在一个连续的内存块中。
(假如unsigned int类型的值在计算机上需要4个字节,上述示例三中的height数组就需要占用24个字节。)
(2)初始化列表中元素的个数不能超过数组的元素个数,否则编译失败。
初始化列表中元素的个数可以小于数组的元素个数,此时没有提供初始值的元素就初始化为0
(对于bool元素数组则为false)
(3)要定义不能修改值的数组,只需要在类型前面加个const关键字。
(4)不能利用赋值语句把整个数组的所有元素赋值到另一个数组的元素。
只能操作数组中的各个元素。
因此,要将一个数组的值复制到另一个数组中,就必须一次复制一个值。此时需要使用循环。
(5)编译器不会检查数组索引值是否有效。
程序员需要自己确保引用的元素不会超出数组边界。
如果使用超出数组有效范围的索引值来存储数据,那么可能会无意中重写内存中的数据,或导致所谓的段错误(segmentation fault)或非法访问(access violation)。
这两个术语的含义是相同的,指操作系统在检测到未授权的内存访问时引发的错误。
无论是哪种情况,程序几乎一定会异常结束。