手撕指针第二章 | 数组
1. 声明和访问
数组是 C++ 表示内存中对象序列最基本的方式。如果你用到的只是内存中一个固定大小、固定元素类型的序列,那么数组完全满足你的要求。
假设有类型 T,T[size] 的含义是「包含 size 个 T 类型元素的数组」。元素的索引范围是 0 到 size-1。
你可以使用下边运算符和指针访问数组元素。下标引用和间接访问表达式是等价的。
数组中元素的数量(即数组的边界)必须是常量表达式,如果你希望边界可变,最好使用 vector。
越界访问数组是一种未定义的行为,而且很有可能会引发严重的程序错误。在 C++ 语言中,运行时边界检查既不常见,也无法保证。
1
2
3
4
5
6
7
8
9
10
11
int main() {
int a;
a = 0;
int arr[] = {1, 2, 3, 4};
printf("%d\n", *arr); /* 1 */
printf("%p\n", arr); /* 0x7ff7b82f8110 */
printf("%d\n", arr[0]); /* 1 */
printf("%d\n", *(arr + 1)); /* 2 */
return 0;
}
在上述代码中,我们把变量 a 称为标量,因为他是个单一的值,类型是一个整数。 而 arr 是一个数组,它是一些值的集合,下标用于标识该集合中某个特定的值。 如 arr[0] 表示 arr 中的第一个值,arr[2] 表示 arr 中的第三个值。
arr[0] 的类型是整型,那么 arr 的类型又是什么呢? 在 C 语言中,几乎所有使用数组名的表达式中,数组名的值是一个指针常量,也就是数组第一个元素的地址,它的类型取决于数组元素的类型。
但,请不要得出「数组和指针是相同的」的结论。指针只是一个标量值,数组具有确定数量的元素(不过编译器并不会对下标值进行有效性检查)。 编译器用数组名来记住这些属性,只有当数组名在表达式当中使用时,编译器才会为它产生一个指针常量。
这个值时指针常量,而不是指针变量,常量的值是不能修改的。不难理解,指针常量所指向的是内存中数组的起始位置,如果修改这个指针变量,唯一可行的操作就是把整个数组移动到内存的其他位置。但是,在程序完成链接之后,内存中数组的位置就是固定的。所以,当程序运行时,再想移动数组就为时已晚了。
C++ 的内置数组本质上是语言的一种底层功能,我们常常用数组来实现标准库 vector 和 array 等更高层级上的、行为定义更好的数据结构。 数组不能执行赋值操作,一旦需要,数组名就会隐式地转换成指向数组首元素的指针。
要避免在接口中使用数组,因为数组名隐式转换是 C 代码和 C 风格的 C++ 代码中很多错误的根源。 如果是在自由存储1,切记一定要在最后一次使用数组之后把对应指针
delete[]掉。
最简单可靠的办法是用资源句柄(string vector unique_ptr)控制自由存储上的数组的生命周期。
如果你是静态2分配数组或者是在栈上分配数组,一定不要
delete[]他。