[cpp primer随笔] 05. 内置数组理解

一、内置数组

在C++中,内置数组属于复合类型的一种,数组元素的个数属于其类型的一部分。

也就是说,数组元素的个数必须在编译时就确定,可以通过无符号整型的字面量或常量表达式进行指定,也可以交由编译器根据初始化列表的元素个数进行推断。

constexpr int arraySize = 5;
int arraySize2 = 5;

int a[5]; // √
int b[arraySize]; // √
int c[arraySize2]; // error: array bound is not an
                   // integer constant before ']' token

1.1 数组初始化

内置数组的初始化分为两种情况:

  1. 默认初始化
    如果不给定初始化列表,则执行默认初始化,此时必须要指定数组元素大小。对于内置类型,若在函数体外将被赋以零值,而在函数体内其值未定义。对于自定义类型,其初值取决于类自身行为(即默认构造函数是否为成员变量提供初值)。
    int a[5]; // 所有元素初始化为0
    void func(){
    	int b[5]; // 元素值未定义
    }
    
  2. 显式初始化
    内置数组可以通过列表初始化来赋初值。
    编译器可以通过列表元素个数,推断数组维度,此时定义中的数组维度可以省略。
    如果在声明符中指定了数组维度,但给出的列表元素个数少于该数值,则缺少的元素将执行函数体外的默认初始化(即内置类型赋零值)。
    int a[] = {1, 2, 3, 4, 5}; // int[5]
    int b[5] = {1, 2, 3, 4}; // b[4] = 0
    

1.2 数组定义语句理解

遵循遵循”自内向外,先右后左“的顺序。
如果需要声明一个指向数组的指针或者绑定数组的引用,需要添加括号,否则变量名均会先向右绑定数组维度,使前面的指针符号用于组成数组元素的类型。

int *a[10]; // 存有10个int指针的数组
int (*b)[10]; // 指向一个长度为10的数组指针
int &c[10]; // error, 不存在引用的数组
int (&d)[10]; // 一个长度为10的数组的引用

1.3 指针与数组的关系

1.3.1 数组名

内置数组本身是一种类型,映射向内存上的一块连续存储空间。只是我们在操作数组时,数组名会被转化为数组首元素的地址,其类型是数组元素类型的指针。
这里补充下,这本质上是一种隐式类型转换。但是当数组名作为decltype的参数,或者&sizeof运、typeid运算符的运算对象时,上述隐式转换不会发生。
在使用类型推断说明符时,auto会将初始值为数组名的变量推断为元素类型指针,只有将变量名用引用符修饰,才会得到目标数组的引用类型。而使用decltype推断数组名,将直接得到数组类型。

int a[5] = {1, 2, 3, 4, 5};
auto p_a = a; // p_a 为 int*
auto &r_a = a; // r_a 为 int[5]
decltype(a) c; // c 为 int[5]

1.3.2 下标运算符

数组名在程序中会被视为数组首元素地址,是一个数组元素类型的指针。指针是一种迭代器,通过数组名,我们可以得到其中任意一个元素的地址。

int a[5] = {1, 2, 3, 4, 5}; // 假设int类型大小为4个字节
int &b = *(a + 4); // 则可计算得到数组中第5个元素的引用

下标运算符[]原理与之相同,a[4]等价于*(a + 4)
然而,与vector这种容器的下标运算符不同,内置数组类型的下标可以为负数,此时依旧等价于对目标指针执行加减运算。如果该指针为数组名,则负数下标对于该数组而言无意义。然而,如果指针指向的是数组中间的某个元素,可以通过负数下标,得到该元素之前的数组元素。

int a[5] = {1, 2, 3, 4, 5};
int *b = &a[4]; // b为a第5个元素的指针
int &c = b[-1]; // c为a第4个元素的引用,b[-1] 等价于 *(b - 1)

1.3.3 数组遍历

int a[5] = {1, 2, 3, 4, 5}; // 定义一个长度为5的整型数组
  • 传统for循环
for(int i = 0; i < 5; i++){
    // ... ...
};
  • 范围for循环(C++ 11)
for(int &i : a){
    // ... ...
}
  • 数组迭代器

与STL容器相同,可以结合首迭代器与尾后迭代器来遍历数组。通过std命名空间中的beginend函数获取内置数组的首尾迭代器,得到的迭代器类型为指针。(指针本身就是迭代器的一种,属于随机访问迭代器)

for(int *i = begin(a); i < end(a); i++){
    // ... ...
}

1.4 多维数组

多维数组不属于内置类型的一种,其本质上是数组的数组
注意两点:

  • 理解多维数组定义,需要自内向外拆解声明符。
int a[3][4]; // 

在上面的定义语句里,首先a是一个有三个元素的数组,然后每个元素又都是一个具有四个元素的数组,并且数组的元素类型为int。

  • 结合范围for语句与auto变量指示符遍历多维数组时,务必加引用符,以便在推导时得到数组类型,否则范围for将不支持对指针进行遍历。
for(auto &i : a){ // 务必将i推导为数组类型,而不能是指针类型。
	for(auto &j : i){
		// ... ...
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值