数组是一种复合类型。由数组的类型、名字、维度构成。其中维度说明了数组中元素的个数,必须大于0,维度在编译的时候一定是可知的,所以维度必须是一个常量表达式。
unsigned cnt = 42; //不是常量表达式
constexpr unsigned cnt1 = 42; /*是一个常量表达式(声明为constexpr的变量一定是一个常量,必须用常量表达式来初始化)*/
int arr[10]; //含有10个整数的数组
int* parr[cnt1]; // 含有42个整数指针的数组
string bad[cnt]; //错误的,因为cnt不是一个常量表达式
string strs[get_size()]; // 当 get_size()是constexpr时才正确,否则是错误的
在定义数组的时候必须指定数组的类型,不可以用auto关键字推导。不存在引用的数组。
默认情况下,数组的元素被默认初始化。
显式初始化数组:
const unsigned sy = 3;
int a1[sy] = { 0,1,2 }; //维度为3的数组,它有3个值分别 为:0,1,2
int a2[] = { 0,1,2 }; //维度是3,可以像这样写,它的空间大小就是由你赋值的多少决定
int a3[4] = { 0,1,2 }; //等价于 a3 [] ={ 0,1,2,0 };后面未初始化的值被默认初始化为0
string a4[3] = { "hi","sy" }; //等价于 a4[] = { "hi","sy", "" }
int a5[2] = { 0,1,2 }; //错误,初始值过多,超出数组空间的大小
当我们在实际写程序的时候,一定要注意下标越界的问题,这个问题很多时候容易被忽视,又不太容易被发现。
在标准库当中数组不允许拷贝和赋值:
int a[] = {0,1,2};
int a1[] = a ;//错误
a2 = a ;//错误
复杂的数组声明:
int arr[10] = { 0,1,2,3,4,5 };
int* ptrs[10]; //ptrs是含有10个整形指针的数组
int& sy[10]; //错误:不存在引用的数组
int(*parr)[10] = &arr; //parr指向一个含有10个整数的数组
int(&parr1)[10] = arr; //parr1 引用一个含有10个整数的数组
修饰符的数量没有特殊限制:
int *( &parr ) [10] = arr; //parr是数组的引用,该数组含有10个指针
我们要理解复杂数组的声明,最好的办法就是从数组的名字开始由内向外的顺序阅读。
访问数组元素:数组可以用范围for或下标运算符访问,要记住一点就是数组下标是从0开始,不要越界。
在使用数组下标的时候,通常将其定义为 size_t 类型。它是一种机器相关的无符号类型,它被设计得足够大可以表示内存中任意对象的大小,它被定义在头文件 cstddef 中。
int arr[ ] = { 0,1,2,3,4,5,6,7,8 };
for (size_t i : arr) //size_t 那里最好使用auto自动推导
{
cout << i << endl;
}
指针和数组
我们可以使用取地址符来获取某个对象的指针,取地址符可以作用于任何对象。对数组使用下标运算符获取的是数组的对象,而使用取地址符就是获取这个元素的指针:
string nums[] = { "one ", "two " , "three" };
string* p = &nums[0];
string* p1 = nums; //这个等价于 string* p1 = &nums[0]; 直接使用数组的名字,数组的名字就是代表指向数组第一个元素的指针
cout << *nums << endl;//解引用数组的首指针得到数组的第一个元素
cout << nums << endl; //输出数组首指针的地址
当我们使用数组作为 auto 变量的初始值的时候,推导得到的类型是指针并不是数组:
int ia [] = {0,1,2,3};
auto ia1(ia); //ia1是一个整形指针,指向ia的第一个元素
ia1 = 41 ; //错误,上面的推导是一个指针类型,不能用整形值给指针赋值
//上面的推导编译器推导的时候是这样的形式: auto ia1(&ia[0]);
当我们使用 decltype 关键字 (选择并返回操作数的类型) 的时候,上面的转换就不会发生:
int ia[] = { 0,1,2,3 };
decltype(ia) ia1; //由decltype返回的类型来定义ia1,所有ia1是一个维度为4的整数数组
ia1[1] = 999; //对数组ia1的第二个元素进行赋值,下标是从0开始
指针也是迭代器,也有迭代器的操作:
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
int* p = arr;
for (size_t i = 0; i < sizeof(arr)/4; i++)
{
cout << *p << " ";
p++;
}
标准库函数 begin 和 end :
C++11新引入了两个名为 begin 和 end 的函数,把数组作为它们的参数,begin负责返回数组的首元素的指针, 而end 负责返回 数组尾元素的下一个位置的指针,符合左闭右开原则 。它们被 定义在 iterator 头文件中。
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
int* Begin_ = begin(arr);
int* End_ = end(arr);
while (Begin_ != End_)
{
cout << *(Begin_) << " ";
Begin_++;
}
使用这两个库函数的时候注意的是:尾指针不能解引用和递增操作。
两个指针想减的结果就是它们之间的距离,参与运算的指针必须指向同一个数组中的元素,它们想减的结果的类型是一种名为 ptrdiff_t 的标准库类型,它也被定义在 cstddef 头文件中,它是一种带符号的类型,它可正可负。
允许两个空指针想减,但不允许它们相加:
int* p = nullptr;
int* i = nullptr;
cout << p -i << endl; //结果为0
下标和指针:
标准库类型限定了使用的下标必须是无符号类型,但是内置的下标运算符没有这个限制。
例如:
int ia[] = { 0,1,2,3,4,5 };
int* p = &ia[2]; //p指向索引为2的元素
int j = p[1]; //p[1] 等价于 *(p+1) ,也就是 ia[3] 表示的那个元素
int k = p[-2]; //p[-2 ] 是 ia[0] 表示的那个元素
数组还有二维数据及二维以上的数组。不过维度太多比较麻烦,一般来说二维就够我们使用了。