定义数组
维度(数组的元素个数)也是数组类型的一部分,必须是编译时已知,因此维度必须是常量表达式
常量表达式
1.字面值常量及其复合运算
20,20+12,"hello"等
2.const变量且用初始值是常量表达式(字面值常量,已知的常量表达式以及它们的复合运算)
const int n=10;
const int m=n+10;
3.constexpr变量,且初始值是常量表达式
constexpr int mf=20;
数组元素的初始化
默认情况下,数组元素被默认初始化。当数组元素是内置类型时:
对于是全局变量的数组,元素都被初始化为0;对于非全局变量的数组,元素含有未定义的值
当数组元素是类时:
若该类有默认构造函数,用默认构造函数初始化,否则必须显示初始化
字符数组的初始化
可以用若干个字符列表初始化数组 char a[]={'a','b'};此时数组a长度为2
也可以用字符串字面值初始化数组 char b[]="ab";此时数组b长度为3,比a多个表示字符串结尾的空字符
数组不允许拷贝和赋值
char c[]=a; //错误,不允许拷贝
char c[2];c=a; //错误,不允许赋值
复杂数组
int arr[10]={1,2,3,4,5,6};
int *ptrs[10]; //ptrs是数组名,它是含有10个整型指针的数组
int (*parray)[10]=&arr; //parray是指针,其指向含有10个元素的数组arr
int (&arrRef)[10]=arr; //arrRef是引用,其引用一个含有10个元素的数组arr
先看数组名,分析是一般数组名还是指针或者引用,按照由内向外的顺序阅读,分析至int *ptrs[10]这种层次,就从右向左理解,也可借助右侧的初始值的类型判断
数组下标
通常将其定义为size_t类型,是机器相关的无符号类型。注意用检查其合法性。应大于等于0且小于数组大小。
遍历数组
可用范围for语句 for(auto i:arr){}
或者用下标for语句 for(size_t i=0;i<10;i++){}
把数组当成指向数组首元素的指针
可以把数组名当作指向数组首元素的指针。编译器就是这么转换的。
auto和decltype作用于数组名得到不同的类型
int a[10];
auto a2(a) 得到a2是整型指针
decltype(a) a3[10]; 得到a3是10个整数构成的数组
定义指向数组第一个元素和最后一个元素的下一个位置的指针
int a[10]={}
int *b=a;(或者int *p=&a[0];) //指向第一个元素
int *e=&a[10];//指向最后一个元素的下一个位置
也可用下面的方法:
int *b=begin(a);
int *e=end(a);
用指针遍历数组
while(b!=e){}
指针也支持所有的迭代器运算
指针 数组名与下标
int a[10]={};
int *p=a;
p[2]等价于a[2],等价于*(p+2)(数组就是指向数组首元素的指针),均指a中第三个元素
p[i]与a[i]不同的在于,p[i]中的i可以是负数,而a[i]中的i只能是非负数
int a[10]={};
int *p=&a[3];
p[-2]等价于a[1]
C风格的字符串
是指存放在字符数组中,以空字符结束的字符串。一般用指针操作这些字符串。
例 char a[]="hello";或char b[]={'a','b','\0'};
操作的函数
定义在cstring(string.h的C++版本)中,常用的有strlen(p),strcmp(p1,p2),strcat(p1,p2),strcpy(p1,p2)它们的参数必须是指针,例如:
strcmp(a,b);
建议:用vector和迭代器,代替内置数组和指针;用string代替C风格的基于数组的字符串
可以用数组初始化vector对象:
int a[]={1,2,3,4,5,6};
vector<int> iv(begin(a),end(a));
可以用C风格字符串初始化string对象,给string对象赋值,或者加法运算的一个运算对象
char a[]="hello";
string s1(a);
string s2;
s2=a;
string s3=a+s1;
不能用vector对象初始化数组
可以用string对象的c_str成员函数得到常量C风格字符串
string s("hello");
const char *str=s.c_str();
多维数组
实质是数组的数组。
int ia[3][4];//ia是有3个元素 的数组,这3个元素每个都是含有4个整数的数组
int arr[10][20][30];//arr是大小为10的数组,它的每个元素是大小为20的数组,这些数组的元素是含有30个整数的数组
多维数组的初始化
二维数组的第一维称作行,第二个维度称作列
int ia[3][4]={
{0,1,2,3},//第一行
{4,5,6,7},//第二行
{8,9,10,11}//第三行
};
遍历多维数组
int ia[m][n];
for(size_t i=0;i<m;i++){
for(size_t j=0;j<n;j++){
ia[i][j]=i*m+j;
}
}
也可用范围for语句
size_t cnt=0;
for(auto &row:ia)
for(auto &col:row){
col=cnt;
++cnt;
}
使用引用的原因是:
1.col写成引用是因为,需要改变col的值
2.row也必须写成引用是因为,若row不是引用,则编译器会将其转换为指向数组首元素的指针,这会引起内层范围for循环不合法。
所以使用范围for循环处理多维数组时,除最内层外,其他所有的控制变量都应该是引用类型
使用指针和数组名遍历多维数组
程序会把多维数组的名字,自动转换为指向数组首元素的指针,例
int ia[3][4];
int (*p)[4]=ia;//ia是&ia[0],而ia[0]是一个含有4个元素的数组,p是指向有4个元素的数组的指针
for(auto p=ia;p!=ia+3;p++){ //p是指向有4个元素的数组的指针,++p使得p指向下一个有4 个元素的数组
for(auto q=*p;q!=*p+4;q++){ //*p是数组,数组名被转换成指向该数组首元素的指针,q指向数组首元素
...
}
}
如果必须写出变量类型的话,借助于以下类型别名
typedef int int_array[4];
或者using int_array=int[4];
则上面的双重for循环可写成
for(int_array *p=ia;p!=ia+3;p++){ //*p是数组 for(int (*p)[4]=ia;p!=ia+3;p++)
for(int *q=*p;q!=*p+4;q++){ //q指向数组首元素
...
}
}
用begin和end更简洁for(auto p=begin(ia);p!=end(ia);p++){
for(auto q=begin(*p);q!=end(*p);q++){
...
}
}