1.数组的大小是固定不变的,声明时必须指定大小(或者使用列表初始化),而且大小必须大于0,C++ Primer里面也建议,如果不确定元素的个数,请使用vector。
int arr[3];int arr1[] = { 0 , 1, 2 }; //数组长度固定为3
2.和内置类型一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值,例如:
int main(int argc, char *argv[]){ int arr[3]; for(int i : arr) std::cout << i << std::endl; getchar(); return 0;}
输出结果为:
3.如果用列表初始化的方式显示的初始化了数组,哪怕没有初始化所有元素,未被初始化的成员也会被赋予默认值,例如:
int main(int argc, char *argv[]){ int arr[3] = { 1,2}; for(int i : arr) std::cout << i << std::endl; getchar(); return 0;}
输出结果为:
4.定义数组时必须指定数组类型,不可以使用auto
5.数组存放的是对象,所以没有引用的数组
6.用字符串给字符数组初始化时需要注意给结尾的空字符留一个位置,像如下语句是错误的:
char arr[4] = "test";
int arr[3] = { 1, 2, 3 };int arr1[] = arr;arr1 = arr;
8.一些理解复杂的数组类型:
int *ptrs[10]; // pArr 是含有10个整形指针的数组int &refs[10]; // 错误,没有引用的数组int arr[10];int (*pArr)[10] = &arr; // pArr指向一个含有10个整数的数组int (&refArr)[10] = arr; // refArr引用一个含有10个整数的数组int *(&arr)[10] = ptrs; // arr是数组的引用,该数组含有10个指针
C++ Primer提供的理解技巧是从数组的名字开始由内向外阅读,像上面的写法应该先从括号里面读。
9.数组的下标通常定义为 size_t 类型,这是一种与机器相关的无符号类型,它被设计的足够大以便表示内存中任意对象的大小。
数组与指针
1.在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。
int arr[3];auto arr1(arr); //arr1是一个整形指针auto arr2(&arr[0]); //arr2是一个整形指针
在某些表达式中则不会出现这种转换,如decltype:
decltype(arr) arr3; // arr3是一个长度为3的整形数组
2.指针也可以当迭代器使用,但是在指向尾元素的下一个位置的时候不能解引用以及递增操作。
int *e = &arr[10]; // e指向arr尾元素的下一个位置std::cout << *e << std::endl; // 错误,不能解引用for (int *b = arr; b != e; b++) std::cout << *b << std::endl;
3.为了能够方便使用指针,C++ 11引入了 begin 和 end 函数,与迭代器的 begin 和 end 作用相似。
int arr[3] = { 0, 1, 2 };int *b = begin(arr); // b指向arr的第一个元素int *e = end(arr); // e指向arr的尾元素的下一个位置auto n = end(arr) - begin(arr); // 和迭代器一样,两个指针相减的结果是距离,n的结果为3
需要注意的是两个指针相减的结果是 ptrdiff_t 类型,和 size_t 一样是与机器相关的,但是因为可能存在负值,所以是有符号的类型。
4.和指针一样,如果给数组名加上一个整数,编译器自动将数组名转换为指向第一个元素的指针,并执行加法操作。
int arr[3] = { 0, 1, 2 };int *p = arr + 1; // p指向arr的第2个元素
5.标准库类型 string 和 vector 的下标都是无符号类型,但是数组的内置下标可以处理负值。
int arr[3] = { 1, 2, 3 };int *p = &a[2]; // p指向数组的第三个元素int k = p[-1]; // p[-1]是数组的第二个元素
C风格字符串
1.字符串字面值是一种通用结构的实例,这种结构即是C++由C继承而来的C风格字符串。它不一种类型,而是为了表达和使用字符串而形成的一种约定俗成的写法,按此书写习惯的字符串存放在字符数组中并以空字符串('\0')结尾。
2.C语言标准库提供了一些可用于操作C风格字符串的函数,但这些函数不负责严重其字符串参数,所以使用时可能会出现错误:
char cArr = { 'C', '+', '+' }; // 不以空字符结束std::cout << strlen(cArr) << std::endl; //严重错误,cArr没有以空字符结束
上述语句会产生未定义的结果,strlen可能会沿着cArr在内存中的位置一直找下去,直到空字符才停下来。
3.两个C风格字符串不可以像 string 一样直接比较,需要使用strcmp()。
const char cArr1 = "string 1";const char cArr2 = "string 2";if(cArr1 < cArr2) {} // 未定义的,试图比较两个无关的地址cArr1 + cArr2; // 错误,在相加两个指针
即使使用strcpy strcat等函数,也很难控制数组的大小问题,所以还是建议使用标准库的string而不是C风格字符串。
使用数组初始化vector对象
1.不允许使用vector初始化数组,但是允许使用数组初始化vector
int iArr[3] = { 1, 2, 3};std::vector iVec(begin(iArr), end(iArr));std::vector isubVec(iArr+1, iArr+2);
多维数组
1.多维数组的初始化规则和一维数组一样。
int iArr[3][4] = { { 0}, { 1}, { 2} }; // 显示初始化每行首元素,其余默认0int iArr1[3][4] = { 1, 2, 3, 4 }; // 显示初始化第一行的元素, 其余行默认0
int iArr[3][4]; int (&row)[4] = iArr[1]; // 把row绑定到iArr的第二个4元素数组上
int iArr[3][4] = { 0 };for (auto &row : iArr) for (auto col : row) std::cout << col << std::endl;
int iArr[3][4];int *p[4]; // 整形指针的数组int (*ip)[4]; // 指向含有4个整数的数组ip = &iArr[2]; // ip指向iArr的尾元素
// 下面两个语句的作用都是将int_array声明为一个4个整数的数组using int_array = int[4];typedef int int_arry[4];