数组和auto的问题。
写在前面的话:可以先看看文章末尾的总结,如果可以明白答案就不必浪费时间。如果想看内容建议先了解一下数组指针的问题,比如大概知道 int(*)[][]、int **、int(*)[]这些都代表什么。
- 问题起源:
在看书时学习了范围for,于是乎就操作一下
int arr[5]={1,2,3,4,5};
for(auto i:arr)
cout<<i<<endl;
跑了一下感觉挺舒服,减少代码长度,看起来也很简洁,而且不用考率数组越界等什么乱七八糟的问题。那么一个二维数组的范围for怎么写呢?我抖了个激灵大手一挥写下下面的代码
int arrt[2][3]={1,2,3,4,5,6};
for(auto i:arr)
for(auto j: i)
cout<<j<<endl;
感觉自己很机智,巧用i来做内层范围,然而编译一下……bang~炸了。网上搜了一下说要这么写
int arrt[2][3]={1,2,3,4,5,6};
for(auto &i:arr)
for(auto j: i)
cout<<j<<endl;
why?仅仅相差一个&(引用),就导致不同的结果。Google一番,各种花式解决问题,但是根本不能说明其到底为什么错误。各种分析含糊其词有甚者大谈特谈int**类型。好吧,幸好我读了一点点书,不然就信了他们的鬼话。于是就只能自己慢慢往出来试了。(我承认博客有错误是正常的,每个人在初学的时候都有理解不透彻的时候,但是在查找很久仍然没有找到结果的我心里很难受,忍不住想要吐槽……这就像现在正在看这个博客的你一样,心里在大骂这个博主戏精一个,废话真多,说了这么多还没讲到重点,浪费我时间……)好好好,废话就不说了我们开始来思考这个问题。
- 多维数组范围for循环遍历时,外层循环变量为什么要引用。
- 这个问题的本质是auto一个数组会是什么结果
#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
int arrt[2][3]={1,2,3,4,5,6};//ok,这是一个二维数组,下面让我们看看arrt到底是个什么玩意
int (*p0)[2][3]=&arrt;//可以看到arrt这个变量的地址是一个int(*)[2][3]型,并不是什么int**,可以用一个这样的指针指向这样的数组。
int (*p1)[3]=arrt;//arrt是一个二维数组,他的数组名表示一个他里面的第一个一维数组的首地址,可以用一个类型int(*)[3](长度为三的一维数组指针)来指向他。
int *p2 =arrt[0];//arrt[0]这是一个一维数组,他表示一维数组的第一个元素的首地址(int *),自然可以让int *来指向他。
//搞清楚这些,我们来看看auto一个数组有什么结果(可以和上面对比)
auto &art0=arrt;//q0,一个二位数组(别名)
auto q0=&arrt; //q1,一个int(*)[2][3](指向一个两行三列的数组指针)
auto q1=arrt; //q2,一个int(*)[3](指向一个长度为三的一维数组指针)
auto q2=arrt[0];//q3,一个int *(指向一个int型)
/* 这里我们可以看出:aout加上&,结果是引用一个数组
auot不加&,结果是其对应的指针
这就是问题的关键,一个是 数组,一个是指针。下面是打印他们的类型。
*/
cout <<"arrt= "<<typeid(arrt).name()<<endl;
cout <<"p0 = "<<typeid(p0).name()<<endl;
cout <<"p1 = "<<typeid(p1).name()<<endl;
cout <<"p2 = "<<typeid(p2).name()<<endl;
cout <<"art0= "<<typeid(art0).name()<<endl;
cout <<"q0 = "<<typeid(q0).name()<<endl;
cout <<"q1 = "<<typeid(q1).name()<<endl;
cout <<"q2 = "<<typeid(q2).name()<<endl;
}
- 理清楚上面的东西后我们就可以继续分析最上面范围for 里的 i 的类型
#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
int arrt[2][3]={1,2,3,4,5,6};//ok,依然这是一个二维数组
/***
for(auto i:arrt)
这里是要把arrt的第一个元素拿出来对i进行初始化,具体看下面
*/
for(auto i:arrt)//auto i=arrt[0];
{
cout <<typeid(i).name()<<endl;
//arrt中有两个成员所以循环两次
}
for(auto &i:arrt)//auto &i=arrt[0];
{
cout <<typeid(i).name()<<endl;
//arrt中有两个成员所以循环两次
}
/***
有了上面的分析,我们很容易知道,第一个i是一个指针(int *)
第二个i是一个数组(引用)
而在范围for循环里面的范围(只能是一个集合,例如数组,string,vector……)是不允许是一个指针的。
所以编译器自然就会报错。
*/
}
OK!大功告成,终于把你们忽悠的看到这里了……嘿~
总结一下,其实就是不加&,i是一个int * 指针,就不能作为下一次范围for 的范围。加了&就是引用一个数组,就可以作为下一次范围for 的范围。
最后吐槽写一篇博客好**累啊,好**麻烦啊。青山不改绿水长流,各位看客咱们有缘再见。
对了如果有朋友觉得上面的知识有问题,有困惑敬请留言,一起讨论学习~。