数组
数组基础
数组是将一到多个相同类型的对象串联到一起所组成的类型,比如
int b[10]; // 类型为int[10]
注意,这里面10是类型的一部分,所以
int b[变量]
是不合法的,数组的声明中只接受可以转换为大于0的整数的常量表达式
数组有两种初始化方式:
- 缺省初始化,会将数组中的每一个元素都按照缺省初始化的方式进行初始化,局部变量初始化为随机值,全局变量、线程相关变量和静态变量初始化为0。比如
int b[10];
- 聚合初始化,比如
int b[3] = {1,2,3}; //初始化为1,2,3 int b[3] = {1,2}; //初始化为1,2,0 int b[3] = {}; //初始化为0,0,0 int b[] = {1,2,3}; //初始化为1,2,3并自动推导b的类型为int[3] int b[]; //不合法的代码 int b[2] = {1,2,3}; //不合法的代码
对于数组的初始化我们需要注意一些事项:
- 不能使用auto来声明数组类型
auto b = {1,2,3}; // b的类型不是int[3],而是std::initializer_list<int>
- 数组是不能复制的
int b[3] = {1,3,4}; int a[3] = b; // 这里会报错
- 数组的元素个数必须是一个常量表达式(编译期可以计算的值)
- 字符串数组具有一定的特殊性
char Str[] = "Hello"; // 类型为char[6],结尾自动添加了一个'\0' char Str[] = {"H","e","l","l","o"}; // 类型为char[5]
除了基本的数组,我们还可以将指针和引用与数组组合形成一些复杂的声明
- 指针数组与数组的指针
int * a[3]; // 指针数组 int (*a)[3]; // 指向数组的指针
- 数组的引用
int b[3]; int (&a)[3] = b;
注意,引用的数组(比如
int & a[3]
)是不合法的
对于数组中元素的访问,通常使用以下方式:
int a[3] = {1,2,3};
a[0];
a[1];
a[2];
在访问数组元素的时候,我们需要注意以下几点:
- 数组对象是一个左值
- 数组在使用时通常会转换成对应的指针类型,指向数组中第一个元素的地址,比如
int a[3]; auto b = a; // 这里的b是int*类型,指向a[0]的地址
- 数组访问的形式
x[y]
会被解析为*((x)+(y))
- 访问时一定要避免数组下标的越界
从数组到指针
使用数组对象时,通常情况下会产生从数组到指针的隐式转换,比如
int a[3];
auto b = a; // 这里的b是int*类型,指向a[0]的地址
也存在数组对象不会转换为指针的时候,比如
decltype
和sizeof
这种隐式转换会丢失一部分信息,最典型的是丢失了数组中包含元素个数的信息。我们可以通过声明引用来避免这种隐式的类型转换,比如
int a[3];
auto & b = a;
以下的图很好的说明了数组和指针的区别
注意,不要使用
extern int* array
之类的语句来声明外部数组变量,只能使用extern int array[]
类似的语句(Unknow Bounded array
,也被叫做不完整的类型)来声明外部数组变量,会报运行期错误。主要的原因是强行将数组内的元素的值作为了指针内保存的地址值,导致指针指向了一个无效的地址。
我们可以使用标准库中的函数来获取指向数组开头和结尾的指针,比如
int a[3] = {1,2,3};
std::begin(a); // 指向数组第一个元素的指针
std::end(a); // 指向数组最后一个元素的下一个元素的指针
std::cbegin(a); // 指向数组第一个元素的指向常量的指针
std::cend(a); // 指向数组最后一个元素的下一个元素的指向常量的指针
注意这几个函数只能针对数组类型(不包括
Unknow Bounded array
)使用,而不能针对数组隐式转换成的指针来使用
我们可以使用指针进行一些算术计算
- 增加、减少
- 比较
大于小于的比较只有针对同一个数组才有意义
- 求距离:两个指针相减会先对地址进行相减,之后除以指针指向的类型的尺寸,得到两个指针指向的对象在数组中的距离
- 解引用(
*
) - 指针索引(
[]
)