目录
8. 数组的[]运算符和标准库类型string、vector的[]运算符的区别
1. 与vector容器的区别
相同点:都是一种能存储类型相同的对象的容器。
不同点:数组的存储对象的大小是确定的,且一旦确定,后续不能超过所定大小的容量,而vector是可以随意添加元素的。
2. 定义
int A[d]; :A是数组名,d是数组所存储的维度即容量大小,注意:因为维度必须已知,所以d只能是常量表达式。一般来说d是无符号整型,但并不是说d不能为负数(理由如下),C/C++,定义了新的类型size_t,它是一种机器相关的无符号类型,被设计的足够大,以便于能够表示内存中任意对象的大小,其在C++中定义在头文件#include<cstddef>中,寻常就使用在表示数组下标以及内存管理相关的函数中。通常数组名,即标识符A所代表的是指向第一个元素的指针。
size_t d=10;
int A[d]; //err 因为d不是常量表达式
const size_t d=10;
int A[d]; //true
constexpr size_t d=10;
int A[d]; //true
int A[10*2-10]; //true
当用指针来指向数组时,会发生隐式转换,这种转换在特定的运算符中是不会发生的,比如:作为decltype关键字的参数、作为 &、 sizeof 以及 typeid 等运算符的运算对象。
int a[10];
int *p=a; // 转换:int *p=&a[0];
3. 初始化
与内置类型一样,在函数体内部定义的数组,没有进行初始化的话,所有的值都是未确定的,在函数体外部定义的数组,数组元素的值都默认初始化,比如:是int类型则初始化为0,string类型则初始化为"",等等。其余,一般初始化方式如下:
int A[4]{0,1,2,3};
int A[4]={0,1,2,3};
int A[4]={0,1,2}; // 0,1,2,3,0
string A[3]={"a","b"}; // a,b,""
char A[3]="ab" //ab
注意:
- 如果初始化的长度小于数组定义的长度的话,则未初始化的值将进行值初始化。
- 字符数组的初始化,如果没有显式的写出空白字符则需要为空白字符留一个位置。如:
const char ok[3]="abc"; //err 没有为空白字符留位置,因为空白字符也占一个位置
const char ok[3]="ab"; //true
4. 存储指针的数组、数组的引用、数组的指针
int arr[10];
int *ptr[10]; //存放指针的数组
int (*ptr)[10]=&arr; //指向arr的数组,注意:他们的容量必须是一样的
int (&ptr)[10]=arr; //arr数组的引用
int *(&ptr2)[10]=ptr; //存放指针的ptr数组的引用
int (*(&ptr2)[10]=ptr; //err,不存在这样的数组,因为,这是指向ptr的引用的数组,但引用不是一个对象,所以不能这样。
建议:阅读数组的复合操作时,从内往外读,如最后一个,先读括号里面的,&ptr2得它是一个引用,然后读*(&ptr2)得它是一个存放指针的数组。
5. 访问数组
一般使用范围for语句来访问数组
constexpr size_t d=3;
int A[d]{0,1,2};
for(auto &c:A)
{
c=10;
}
6. 标准库中的begin()和end()函数
定义在头文件#include<iterator>中,这里的begin()和end()函数与容器里的函数不是同一个,同名,不过功能相似,标准库里的begin()返回的是数组里的首元素的指针,end()返回是数组尾元素的下一个元素的指针。使用方式:
constexpr size_t k=3;
int A[k]={0,1,2};
int *b=begin(A);
int *e=end(A);
7. 指针的运算
指针的运算与迭代器的运算是相似的,同样有比较、与整数相加、两个指针相减、递增等,也不存在两个指针相加的运算。但是,两个指针相减返回的类型是一个名为ptrdiff_t的标准库类型,其和size_t一样,存在于cstddef头文件中。与size_t不同的是,ptrdiff_t是有符号整型,因为,两个指针相减是有可能产生负值的。
8. 数组的[]运算符和标准库类型string、vector的[]运算符的区别
数组的[]运算符,的下标可以是有符号类型也可以是无符号类型,但标准库类型的下标就必须是无符号类型。
int a[10]={0,1,2,3,4,5,6,7,8,9};
int *p=a+3;
int k=p[-2]; //k=1;
9. 多维数组
9.1 概念
严格来说,C++中没有多维数组,常说的多维数组实际上是数组的数组,应像读指针数组和数组指针那样从内往外读,如int a[3][4],的意思是a首先是一个容量为3的整型数组,然后每一个元素里,又存有一个容量为4的整型数组。同时因为数组的本质其实是指针的操作,所以:二维数组里,每一层存储的都是另一个数组的首指针!如int a[3][4],a的第一层是容量为3的整型数组,这个数组里面的每个元素都存储的是容量为4的数组的首指针。
9.2 多维数组的初始化
多维数组的初始化和一维数组类似,都符合如下特点:
- 如果在全局范围内定义,则默认初始化所有元素值为0,若在局部范围内定义,则不会进行默认初始化。
- 若只初始化了首元素,则其余的元素全部进行默认初始化。
int a[3][3]=
{
{0},
{1},
{2}
}; // 0 0 0
// 1 0 0
// 2 0 0
iny a[3][3]={0};// 0 0 0
// 0 0 0
// 0 0 0
9.2 范围for语句处理多维数组
int ia[3][4] = { 0 };
for (auto &arr : ia)
{
for(auto &col:arr)
{
col=1; //将数组元素全部改变为1
}
}
注意:当使用范围for语句只访问多维数组时,除了最内层循环外,都必须要是引用类型!!
int ia[3][4] = { 0 };
for (const auto &arr : ia) //有无const都可以,只是,这里写上const是为了强调,二维数组的这个行是常量,不可修改。
{
for(auto col:arr)
cout <<col<<endl;
}
因为:以此二维数组ia为例,当第一层循环不是引用类型时,比如是 auto arr : ia 则此时,数组会被自动转换为指针,auto arr = &ia[0][0] 等价于 auto arr = ia[0] ; 这样的话得到的就是ia二维数组的第一个元素即容量为4的数组的首地址,这样 auto 就会自动转化为 int * 的类型,如果转为了 int * 类型,则到了下一层,此时,arr已经不是数组了,而是一个int型的指针,而对int型的指针进行遍历,显而易见的是不行的。(也可以这样理解,范围for循环处理数组时的机制是使用数组的begin()和end()函数,而一个 int * 指针并不是这两个函数的有效参数,所以不行)。
9.3 指针和多维数组
与一维数组一样,多维数组的标识符,表示着,第一层的第一个数组的首指针。
int ia[3][4];
int (*p)[4] = ia; // p是指向ia[0]一个容量为4的整型数组的指针
p = &ia[2]; // p此时修改为指向ia[2]一个容量为4的整型数组的指针
9.3.1 使用指针来访问多维数组
因为C++新标准引入了 auto 和 decltype 关键字,所以,访问时,可以避免在数组面前加上一个指针类型了。
int ia[3][4];
for(auto p=ia;p!=ia+3;p++)
{
for(auto q=p;q!=p+4;q++)
{
cout<<*q<<" ";
}
cout<<endl;
}
或者使用begin()和end()函数进行访问也可以,注意begin()和end()里取的是 *p,因为p此时是a的指针,而不是a的元素里数组的指针,所以用 *p 。
注意:什么时候取*,什么时候不取* ,要斟酌下,一般非最内层的都要取 * 。
int a[3][4];
for(auto p=begin(a);p!=end(a);p++) //第一层,p:0~3
{
for(auto q=begin(*p);q!=end(*p);q++) //第二层,q:0~4
{
cout<<*q<<" ";
}
cout<<endl;
}
9.3.2 类型别名简化多维数组的指针
using int_array=int[4]; // 等价于 typedef int int_array[4];
for(int_array *p=ia; p!=ia+3;p++)
{
for(int *q=*p;q!=*p+4;q++)
cout<<*q<<" ";
cout<<endl;
}
将一个容量为4的int型数组命名为 int_array ,用其来控制循环的变量。
总结:
#include<iostream>
#include<vector>
#include<cstddef>
using namespace std;
int main()
{
int ia[3][4] = {
{1},
{2},
{3}
};
//1
for (auto &i : ia)
{
for (auto j : i)
cout << j << " ";
cout << endl;
}
//2
for (size_t i = 0; i < 3; i++)
{
for(size_t j=0;j<4;j++)
cout << ia[i][j] << " ";
cout << endl;
}
//3
for (auto p = begin(ia); p != end(ia); ++p)
{
for (auto q = begin(*p); q != end(*p); ++q)
cout << *q << " ";
cout << endl;
}
//4
typedef int int_array[4];
for (int_array *i = ia; i != ia + 3; ++i)
{
for (int *j = *i; j != *i + 4; ++j)
cout << *j << " ";
cout << endl;
}
}