与 vector
类型相同,数组也是存放类型相同的对象的容器,与 vector
不同的是,数组的大小是确定不变的,不能随意向数组中添加元素。
定义和初始化内置数组
数组的维度是编译时已知的,维度必须是一个常量表达式。
和内置类型一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。
显示初始化数组元素
- 可以对数组元素进行列表初始化,此时允许忽略数组的维度,如果没有明确指明数组的维度,编译器会根据初始值的数量计算并推测出来。
- 如果指明了数组的维度,那么初始值的总数量不能超出指定的大小。
- 如果维度比提供的初始值数量大,则用提供的初始值初始化靠前的元素,剩下的元素被初始化为默认值。
const unsigned sz = 3;
int ia1[sz] = { 0,1,2 }; //ia1含有三个元素
int a2[] = { 0,1,2 }; //a2 是含有三个元素的数组
int a3[5] = { 0,1,2 }; //a2 是含有5个元素的数组,前三个元素分别是0,1,2,后两个元素默认初始化
int a4[2] = { 1,2,3 }; //错误,初始值过多
字符数组的特殊性
字符数组可以使用字符串字面值进行初始化,当使用这种方式时,一定要注意字符串字面值末尾还有一个空字符,这个空字符也会像字符串其它字符一样被拷贝到字符数组中。
char a1[] = { 'C','+','+' }; //列表初始化,没有空字符
char a2[] = { 'C','+','+',' '}; //列表初始化,含有显示的空字符
char a3[] = "C++"; //自动添加表示字符串结尾的空字符
const char a4[6] = "Daniel"; //错误,没有空间容纳空字符
不允许拷贝和赋值
不能将数组的内容直接拷贝给其它数组作为初始值,也不能用数组为其它数组赋值:
int a[] = {0,1,2};
int a2[] = a; //错误,不允许使用一个数组初始化另一个数组
a2 = a; //错误,不能将数组直接赋值给另外一个数组
访问数组元素
数组元素可以使用范围 for
语句或下标运算符来访问。
在使用数组下标的时候,通常将其定义为 size_t
类型,size_t
是一种与机器无关的无符号类型,它被设计的足够大以便能表示内存中任意对象的大小。size_t
定义在头文件 cstddef
中。
指针和数组
数组的元素也是对象,对数组元素使用下标运算符得到该数组指定位置的元素,对数组元素使用取地址符就能得到指向该元素的指针。
string nums[] = { "one","two","three" };
string *p = &nums[0];
数组还有一个特性:很多用到数组名的地方,编译器会自动将其替换成为一个指向数组首元素的指针。
string *p2 = nums; //等价于p2 = &nums[0]
使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组。
使用decltype关键字时,编译器不会将数组以指针看待。
int ia[] = {0,1,2,3,4,5,6,7,8,9};
auto ia2(ia); //ia2是一个整型指针,指向ia的第一个元素
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};
标准库函数 begin 和 end
C++11新标准引入的两个函数 begin
和 end
。因为类不是类类型,因此这两个函数不是成员函数,正确的使用方法是将数组作为它们的参数。
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(ia);
int *end end(ia); //返回数组尾元素下一个位置的指针
指针运算
指针的基本运算包含:解引用,递增,比较,与整数相加,两个指针相减。
- 两个指针相减的前提是两个必须指向同一个数组当中的元素,两个指针相减的结果是
ptrdiff_t
的标准类型,它定义在 头文件<cstddef>
中,ptrdiff_t
是一种带符号类型。、 - 给指针加上一个整数,得到的新指针仍需指向同一数组的其它元素,或者指向同一数组的尾元素的下一个位置。
- 只要两个指针指向同一个数组的元素,或者指向该数组的尾元素的下一个位置,就能利用关系运算符对其进行比较。
下标和指针
int ia = {0,2,4,6,8};
int i = ia[2]; //ia[2]是(ia+2)所指向的元素
int *p = ia; //p指向ia的首元素
i = *(p+2); //等价于ia[2]
int *p = &ia[2]; //p指向索引为2的元素
int j = p[1]; //p[1]等价于*(p+1),就是ia[3]表示的那个元素
int k = p[-2]; //p[-2]等价于就是ia[0]表示的那个元素
C 风格字符串
尽管C++支持C风格字符串,但在C++程序中最好不要使用C风格字符串,这是因为C风格字符串不仅使用起来很不方便,而且极易引发程序漏洞,是诸多安全问题的根本原因。
C 标准库string函数
C语言标准库提供了一组函数,这些函数可以用于操作C风格字符串,它们定义在 <cstring>
中。
传入此类函数的指针必须是指向以空字符结束的字符数组。
混用string对象和C风格字符串
任何出现字面值的地方都可以用以空字符结束的字符数组来替代:
- 允许使用以空字符结束的字符数组来初始化
string
对象或为string
对象赋值。 - 在
string
对象的加法运算中,允许使用以空字符结尾的字符数组作为其中一个运算对象。
但是如果程序中需要使用一个C风格字符串,不能直接使用 string
对象来代替它。
不能用string对象直接初始化指向字符的指针,为了完成这个功能,string
提供了 c_str
的成员函数,c_str
函数的返回值是一个C风格字符串,结果指针的类型是 const char*
。
使用数组初始化vector
不允许使用一个数组为另一个内置类型的数组赋值。不允许使用vector对象初始化数组,但是可以使用数组来初始化vector对象。要实现这一目的,只需指明要拷贝区域的首元素地址和尾元素地址。
int int_arr[] = {0,1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr));
vector<int> subVec(int_arr + 1,int_arr + 4); //也可以只用数组的一部分初始化vector
注意:
现代C++程序应该尽量使用 vector
和迭代器,避免使用内置数组和指针;应该尽量使用 string
,避免使用C风格字符串。