数组是一种类似于标准库类型vector 的数据结构。与vector 不同的地方是,数组的大小确定不变,不能随意向数组中增加元素。因为数组的大小固定,因此对某些特殊的应用来说程序的运行时性能较好,但是相应地也损失了一些灵活性。
{本章内容早有接触,笔记较为随意}
3.5.1 定义和初始化内置数组
【数组是一种复合类型。数组的声明形如a[d],其中a 是数组的名字,b 是数组的维度。数组中元素的个数也属于数组类型的一部分,编译的时候维度应该是已知的。维度必须是一个常量表达式(2.4.4)】
【和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。
如果在函数体外部,这些内置类型元素会被初始化成默认值。
不允许将数组的内容拷贝给其他数组作为其初始值,也不允许用数组为其他数组赋值。】
【定义数组的时候必须指定数组的类型,不允许使用auto 关键字由初始值的列表推断类型。(decltype 关键字推断类型是允许的)
另外和vector 一样,数组的元素应为对象,因此不存在引用的数组。】
【可以对数组的元素进行列表初始化,此时允许忽略数组的维度。
const unsigned sz = 3;
int ia1[sz] = {0, 1, 2}; // 合法,sz 是常量表达式
int a2[] = {0, 1, 2}; // 合法,编译器根据初始值的数量构造了维度为3 的整形数组
int a3[5] = {0, 1, 2}; // 合法,编译器会对没有匹配的元素初始化成默认值
】
【字符数组有一种额外的初始化形式,我们可以用字符串字面值对此类数组初始化。当时用这种方式时,一定要注意字符串字面值的结尾处还有一个空字符,这个字符也会像字符串的其他字符一样被拷贝到字符数组中去。
char a3[] = "C++" // 最终a3 是4 个元素的数组
char a4[3] = "C++" // 报错,数组界限溢出
】
【在复杂的定义变量的语句中,与名字近的修饰符先考虑,同样近时先考虑右边(待修正):
int arr[10];
int *ptr[10]; // ptr 是一个含有10 个指向整形变量的指针的数组
int (*ptr1)[10]; // ptr1 是一个指向含有10 个整形变量的数组的指针
int (&ref)[10] = arr; // ref 是一个含有10 个整形变量的数组的引用
int *(&ref1)[10] = ptr; // ref1 是一个含有10 个指向整形变量指针的数组的引用
】
3.5.2 访问数组元素
【在使用数组下标的时候,通常将其定义为size_t 类型。size_t 类型是一种机器相关的无符号类型(???),它被设计得足够大以便能表示内存中任意对象的大小。】
【大多数常见的安全问题都源于缓冲区溢出错误。当数组或其他类似数据结构的下标越界并试图访问非法内存区域时,就会产生此类错误。
与vector 和string 一样,当需要遍历数组的所有元素时,最好的办法也是使用范围for 语句。】
3.5.3 指针和数组
【在很多用到数组名字的地方,编译器都会自动地将其替换为一个指向数组首元素的指针。
在一些情况下,对数组的操作实际上是对指针的操作,如:使用数组作为auto 变量的初始值时,推断得到的类型是指针而非数组(使用decltype 仍为数组)。】
【指针也是迭代器,vector 和string 的迭代器支持的运算,数组的指针全都支持。
使用超过数组规模的下标,可以得到数组的尾后指针。】
【c++11 新标准引入了两个名为begin 和end 的函数,这两个函数不是数组的成员函数(数组毕竟不是类类型),正确的使用方式是将数组作为它们的参数:
int ia[] = {1, 2, 3, 4, 5, 6, 7, 8};
int *beg = begin(ia); // 指向ia 首元素的指针(同ia)
int *last = end(ia); // 指向ia 尾元素的下一元素的指针
两个函数均定义在iterator 头文件中。】
【在数组使用下标运算符时,实际上是将数组作为指针进行运算并解引用,最终得到下标位置元素的引用(对于指向数组中元素的指针使用下标运算符,同样如此):
int *p = &ia[2];
int j = p[1]; // 元素ia[2] 作为了j 的初始值
int k = p[-2]; // 元素ia[0] 作为了k 的初始值
内置的下标运算并不要求索引严格为无符号类型,它可以处理负值。这点与vector 和string 并不相同。】
3.5.4 C 风格字符串
const char ca1[] = "HELLO";
const char ca2[] = "bye-bye";
if (ca1 < ca2) // 未定义的:试图比较两个无关地址
要想比较两个C 风格字符串需要调用strcmp 函数:
if (strcmp(ca1, ca2) < 0) // 如果两个字符串相等,函数返回0;前者较大,返回正值;后者较大,返回负值
要想连接两个C 风格字符串,需要调用strcpy 和strcat 函数(不赘述)。】
3.5.5 与旧代码的接口
const char *str = s.str();
c_str 函数的返回值是一个C 风格字符串。就是说,函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,而这个数组所存的数据恰好与string 对象的一样。结果指针的类型是const char,从而确保我们不会改变字符数组的内容。
int arr[] = {0, 1, 2, 3, 4, 5, 6};
vector<int> ivec(begin(arr), end(arr));
只需指明要拷贝区域的首元素地址和尾后地址,就可以实现用数组来初始化vector 对象的操作。当然创建vector 对象的初值也可以仅是数组的一部分,只要地址对应改变即可。】