《c++ primer》3.5 array 小结
array的长度是静态的,即在编译阶段就已经确定。没有 vector 灵活,但性能一般更强。
1. 声明
维数必须是一个 constant expression,另外数组中每个单元必须指明类型(不能用auto),必须是对象,不能是别名(reference)。
1 int arr[10]; 2 int *parr[sz]; // 42 个整数指针 3 string str[ get_size() ]; // 如果 get_size() 返还常数表达式就ok,否则错误
2. 初始化
int ia1[3] = {0,1,2}; int ia1[] = {0,1,2}; // 自动确定维数为3 int ia1[5] = {0,1,2}; // 自动将后2个元素初始化为0 string a4[3] = {"hi", "bye"}; //自动将最后一个元素初始化为""
3. 字符数组
字符数组多一个隐含的元素 \0 在末尾,作为结束的标志。
char a1[] = {'C', '+', '+'};//长度实际为4,自动增加 '\0' char a4[6] = "Daniel"; //错误,没有空间给隐含的 '\0'
4. 指向数组的指针
int * ptrs[10]; // ptrs 是10个整数指针构成的数组 int (*Parray) [10] = & arr; // Parray 是指向数组arr 的指针,arr由10个整型构成 int (&arrRef) [10] = arr; // arrRef 是数组 arr 的别名,arr 由10个整型构成 int * (&arry) [10] = ptrs; // ptrs 是 10 个指向整型的指针构成的数组,arry 是它的别名
5. 引用数组的单元
可以用下标,也可以用 range for。下标范围是 0, 1, n-1,其中n为维数。下标应为 size_t 类型,size_t 无符号,可以足够大,在 cstddef 头文件中。
如果要遍历所有单元,用 range for 是更好的选择,
unsigned scores[11] = {}; for( auto i: scores) cout<< i << " "; cout<<endl;
6. 指针与数组
实际上,数组就是用指针实现的。
string num[] = {"one", "two", "three"};// num 也是一个指针,指向第一个元素
7. begin, end 函数
指针是一种迭代器,但数组不是类,所以没有成员函数 begin(), end(),于是 library 提供了 begin(arr), end(arr) 函数
int *pbeg = begin(arr), *pend = end(arr); while( pbeg != pend && *pbeg >=0) // 寻找整数数组中第一个负整数 ++pbeg;
不可以dereference 或者增加 end(arr)指针,因为 end(arr) 指向的是数组外的地址。
因为指针是迭代器,所以其他迭代器的操作也适用,比如加上一个整数(若指向数组外,除了end(arr)以外的值是错误的),两个指针相减,比大小。
下标符号[] 可以用在任何指针上,只要得到的元素再数组内。
int ia[] = {0,2,4,6,8}; int *p = & ia[2]; // p 指向元素4 int k = p[-2]; // k = ia[0] = 0
所以下标可以是负整数,只要得到的元素仍在数组内就行。
8. C风格的字符串
我记得谭浩强老师的C++教程就是沿用的C风格的字符串,它是如下表示的:
char a[80] = "hahaha"; // 自动用'\0'结束,即 a[6]='\0'
默认用 '\0' 表示字符串内容的结束,数组长度必须大于等于字符串长度+1。
library提供了一些函数对这种风格的字符串进行操作
strlen(p) 字符串p的长度,不包括'\0'即null字符 strcmp(p1,p2) 比较两个字符串,①比较长度,②按字典顺序比较字符 strcat(p1,p2) 将 p2 的内容追加到 p1,然后返回 p1 strcpy(p1, p2) 将 p2 的内容赋值给 p1,返回 p1
上面这些函数都不能保证字符数组长度够用,字符数组长度够用必须由程序员来手动保证。
所以很容易发生错误。c++提倡使用 vector 和 string类型,而不是 数组 和 C风格的字符串。但有些代码是用后者写的,需要跟那些代码打交道,所以c++的 library 提供了办法处理这些过时的对象。
const char * str = s.c_str(); // string 类 s 的内容变成一个C风格的字符数组并返还首字符的指针
可以调用 string 的成员函数 c_str,返还一个C风格字符数组的指针,但如果此后改变了 string s,这个指针可能会出现错误,所以改变之前先 copy str[] 里面的内容。
9. 用数组来初始化vector
int int_arr[] = {0, 1, 2, 3, 4, 5}; vector<int> ivec( begin(int_arr), end(int_arr) ); // ivec 的内容为 arr 中的所有成员 vector<int> subVec( int_arr+1, int_arr+4 ); // subVec 的内容为 1,2,3,4
10. 多维数组
没有多维数组,多维数组实则是基本单元是数组的数组。
int ia[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //编译器自动分行 int ia[3][4] = {0,3,6,9}; //第一行元素为 0,3,6,9,其他元素赋0 (value-initialized)
可用 range for 来遍历多维数组
size_t cnt = 0; for( auto &row : ia) for( auto &col : row){ col = cnt; cnt++; }
这里 row, col 必须用别名,否则,ia 的成员是指向 4元素数组 的指针,row 会是一个这样的指针,但没有下一步单元,所以内层循环会错误。
所以,用 range for 来遍历多维数组时,除了内层以外,其他各层的循环变量都必须是别名。所以全用别名就完事了。