数组是一种类似于标准库类型vector的数据结构,它的大小确定不变,不能随意向数组中添加元素。
定义和初始化数组
数组的声明形如a[b],a是数组的名字,b是数组的维度,表示数组中元素的个数,注意b必须要是常量表达式:
int i=10; //不是常量表达式
constexpr int sz=10; //是常量表达式
int a[10]; //含有10个整数的数组
int *p[sz]; //含有10个整型指针的数组
string s[i]; //错误,s不是常量表达式
可以对数组进行列表初始化,此时可以忽略数组的维度,如果指定了数组的维度,则初始值的数量不能超出指定大小,如果小于指定大小,则先初始化靠前的元素,剩下的元素被初始化成默认值:
int a1[3]={1,2,3};
int a2[]={1,2,3}; //数组维度是3
int a3[5]={1,2,3}; //等价于a3[]={1,2,3,0,0}
string s[3]={"a","the"}; //等价于s[]={"a","the",""}
int a4[3]={1,2,3,4}; //错误,初始值过多
int a5[]=a1; //错误,不允许用一个数组初始化另一个数组
注意字符数组的特殊性:
char a1[]={'a','b','c'}; //列表初始化,没有空字符
char a2[]={'a','b','c','\0'}; //列表初始化,有初始值
char a3[]="abc"; //自动添加空字符表示字符串结束,此时数组维度是4
char a4[3]="abc"; //错误,没有空间放空字符
复杂的数组声明
int arr[10];
int *a1[10]; //p是含有10个整型指针的数组
int &a2[10]; //错误,不存在引用的数组
int (*a3)[10]=&arr; //a3指向一个含有10个整型指针的数组
int (&a4)[10]=arr; //a4引用一个含有10个整型指针的数组
a1和a2可以从右向左依次绑定,但对于a3和a4则要由内向外阅读。*a3说明a3是一个指针,接下来看右边,说明a3指向的是一个大小为10的数组,最后看左边,可知数组中的元素是整型。
指针和数组
在很多时候,编译器会将数组名自动替换为指向数组首元素的指针:
int a[]={1,2,3};
int *p1=&a[0]; //p1指向数组的第一个指针
int *p2=a; //等价于p2=&a[0]
只要指针指向的是数组中的元素(或是数组中尾元素的下一位),都可以执行下标运算,下标可以是负数:
int a[5]={1,2,3,4,5};
int *p=&a[2];
int j=p[1]; //p[1]等价于*(p+1),就是a[3]所表示的那个元素4
int k=p[-1]; //p[-1]等价于*(p-1),就是a[1]所指的那个元素2
多维数组
严格来说,C++中并没有多维数组,列如我们所说的二维数组实际上是数组的元素还是数组:
int a1[3][4]; //a1是大小为3的数组,每个元素是大小为4的数组
int a2[10][20][30]; //a是大小为10的数组,每个元素
都是大小为20的数组,这个大小为20的数组中的元素是含有30个整数的数组
多维数组的初始化:
int a1[3][4]={
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
int a1[3][4]={0,1,2,3,4,5,6,7,8,910,11}; //与上一个等价
int a1[3][4]={{0},{4},{8}}; //初始化每行的第一个元素
int a1[3][4]={1,2,3,4,5,6}; //依次初始化前6个元素,其余的元素被默认初始化
多维数组的下标引用:
如果表达式含有的下标运算符数量和数组的维度一样多,则表达式的结果是给定类型的元素,如果下标运算福数量少于数组维度,则表达式结果是给定索引出的一个内层数组:
int (&row)[4]=a1[1]; //row是数组的引用,引用的是ia的第二个含有4个元素的数组
使用范围for语句处理多维数组:
size_t cnt=0;
for(auto &row:a1) //对于外层数组的每一个元素
for(auto &col:row){ //对于内层数组的每一个元素
col=cnt; //将cat赋值给该元素
++cnt;
这里因为改变了数组元素的值,所以使用了引用,但是即使不改变数组元素,第一个for语句也要使用引用,这是为了避免数组自动转换成指针:
for(auto &row:a1)
for(auto col:row)
cout<<col<<endl;