數組和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 的範圍。
最後吐槽寫一篇博客好**累啊,好**麻煩啊。青山不改綠水長流,各位看客咱們有緣再見。
對了如果有朋友覺得上面的知識有問題,有困惑敬請留言,一起討論學習~。