只能说它一点也不省心,由内到外
1、从下标来说
使用数组下标时,其类型为size_t类型。是一种机器相关的无符号整型,它范围足够大以至于能表示内存中任意对象的大小。它定义在cstddef头文件中。
对比:string类型
下标运算符【】:接收的输入是string::size_type类型的值,是一种与机器无关的无符号整型,能存放下任何string对象的大小,表示要访问字符的位置,返回该字符的引用.它是string类的配套类型,与机器无关的特性。
2、初始化问题
#include <iostream>
using namespace std;
string sa[10]; //数组中的元素均为空串
int ia[10]; //数组中的元素均为0
int main(){
string sa2[10]; //数组中的元素均为空串。
int ia2[10]; //数组中的元素未定义。
}
3、与指针,引用
使用数组的时候编译器一般把它转换成指针(对数组的元素使用取地址符就能得到指向该元素的指针),同时,在很多用到数组名字的地方,编译器会自动将其替换成一个指向数组首元素的指针(对数组执行下标运算其实是对指向数组元素的指针执行下标运算)。
在使用auto和decltype时,数组分别被认为是 指针 、数组 。
int main(){
inta[]={0,1,2,3,4,5};
auto k=a; //auto k=&a[0] k是指针
decltype(a) k={0,1,2,3,4,5}; //k是数组
return0;
}
4、本质:迭代器
指针也是迭代器。使用指针遍历数组:
先获取指向数组元素第一个元素的指针和指向数组元素尾元素的下一位置的指针。
如何获取指向数组元素尾元素的下一位置的指针:我们可以设法获取数组尾元素之后的那个并不存在的元素的地址。
int *e=&arr[10];//指向arr尾元素的下一位置的指针 (共10个元素)
for(int*b=arr;b!=e;++b)
cout<<*b<<endl;
我们用下标运算符索引了一个不存在的元素。形如尾后迭代器,我们的尾后指针不指向任何元素,它指向一个提供地址初始化e的不存在的元素。我们不能对它解引用或递增。
对首位指针的进一步说明:
标准库中的begin()和end()函数定义于iterator头文件中,可以对数组类型进行操作,返回其首尾指针。
int *beg=begin(arr); //指向arr首元素的指针
int *last=end(arr); //指向arr尾元素下一位置的指针
/*找到a中的第一个负数*/
int*beg=begin(arr),*end=end(arr);
while(beg!=end&&*beg>=0)
++beg;
指向数组元素的指针可以执行我们已知的所有迭代器运算且意义一致。
和迭代器一样,两个指针相减是它们之间的距离,类型为名为ptrdiff_t的标准库类型(ptrdiff_t是带符号类型),和size_t一样,定义在cstddef头文件中,与机器相关。
int *p=a+6; //指向数组尾元素的下一位置
以上不依赖于对象是数组,即其他类型指针同样有类似加减操作。对于空指针,允许给空指针加或减一个值为0的整型常量表达式;两个空指针也允许相加减,结果为0.
int*p=&a[2]; //p指向索引为2的元素,a[2]
int j=p[1]; //即*(p+1),即a[3]
int k=p[-2]; //即*(p-2),即a[0] 注意这里下标可以为负,vector和string中的不可以
5、字符数组
在C语言中定义了字符数组,形如char ca[]={'C','+','+'}; 因为本质上属于数组,当使用数组时实质上使用的是指向数组的指针,因此不支持两个字符数组的比较,拷贝,连接及计算一个字符数组长度的操作。此时我们可以用一些函数完成:
strlen(p);//返回长度,不计空字符
strcmp(p1,p2);//比较P1和P2(p1>p2为正,P1==P2为0,else为负)
strcat(p1,p2);//连接P2到P1后,返回P1
strcpy(p1,p2);//将P2拷贝到P1,返回P1
运用这些函数有个条件,就是字符数组最后一位必须是空字符(结束标志),(这样才可以让函数在内存中探寻,直到遇到空字符停下)
在我们使用strcat等函数时,需要提前设定P1的大小,确保它足够大,但这里常会出错,因此不建议使用C语言中定义的字符数组(亦称C风格字符串),虽然C++中也兼容。
const char ca[] = {‘h‘,’e’,’l’,’l’,’o’};
const char *cp=ca;
while(*cp){
cout<<*cp<<endl;
++cp;
} //打印出hello后出现乱码
const char ca[]={‘h‘,’e’,’l’,’l’,’o’,‘\0’};
const char *cp=ca;
while(*cp){
cout<<*cp<<endl;
++cp;
} //乱码消失
6、与string对象
之前我们认为: strings("world");//"world"中有6个字符,string中有5个
这是它们初始化的方式,即右值以空字符结束,string对象赋值忽略空字符。实际上它的定义可以扩展,任何以空字符结束的,不仅是字符串常量,都可以对string对象赋值,忽略空字符存到string对象中。同时,允许以空字符结束的字符数组参与string的运算(即“以空字符结束的字符数组“不仅可以赋值string对象,还可以参与它的运算),只是它不能同时占据两个运算对象(因为此时就是两个字符数组相加了,显然是不允许的)。
但是反之,若想用一个string对象初始化以空字符结束的字符数组,就不被允许了。(不能用对象初始化指针)当然我们同样定义了执行这种功能的操作:const char *str=s.c_str();
c_str()的返回值是一个C风格的字符串。//当改变S值会使c_str返回的数组失效,若想继续使用需提前拷贝
我们可以用数组初始化vector对象,用一个vector对象初始化vector对象,但对于数组,以上皆为非法。
用数组初始化vector对象:
int a[]={1,2,3,4,5,6};
vector<int> ivec(begin(a),end(a)); //begin指向首地址,end指向尾元素的下一位置
vector<int> svec(a+1,a+4); //3个元素
声明:C++中尽可能使用vector,迭代器和string,而不是数组,指针和字符数组。
7、数组的数组
数组的数组:使用范围for语句处理数组的数组,除了最内层的循环,其他所有循环的控制变量都应是引用类型。(因为会被编译器认为是指针)
/*将元素的值设为该元素在整个数组中的序号*/
const int x=5,y=6;
int a[x][y];
size_t b=0;
for(auto&row:x){
for(auto&col:y){
col=b;
++b;
}
}
8、安全问题
大多数安全问题都源于缓冲区溢出错误,当数组下标越界并试图访问非法内存区域时,就会产生此类错误。(即使编译通过,也不保证无此类错误)所以建议遍历数组同样用范围for语句。