C++?多維數(shù)組詳解
3.6 多維數(shù)組
其實C++中沒有什么多維數(shù)組,所說的多維數(shù)組其實就是數(shù)組的數(shù)組。
當一個數(shù)組的元素依舊是數(shù)組時,通常使用兩個維度來定義它:一個維度表示數(shù)組本身大小,另外一個維度表示其元素(數(shù)組的數(shù)組)大寫:
int ia[3][4];
大小為3的數(shù)組,每個元素是含有4個整數(shù)的數(shù)組。
int arr[10][20][30] = {0};
大小為10的數(shù)組,它的每個元素都是大小為20的數(shù)組,這些數(shù)組的元素又包含30個整數(shù)的數(shù)組,最后將所有元素初始化為0。
由內到外的順序閱讀此類定義有助于更好地理解其真實含義。在第一條語句中,定義名稱為ia,顯然ia是一個含有3個元素的數(shù)組。再往右邊寫著ia的元素的維度,所以ia的元素本省又是含有4個元素的數(shù)組。在觀察最左邊,就能知道真正儲存的元素(ia的元素的數(shù)組)是整數(shù)。因此最后可以明確第一條語句的含義:定義了一個大小為3,名為ia的數(shù)組,該數(shù)組的每個元素都是含有4個整數(shù)的數(shù)組。
再用同樣的方式理解第二條arr的定義,首先arr是一個大小為10的數(shù)組,它的每個元素都是大小為10的數(shù)組,而這些數(shù)組的元素又都是含有30個整數(shù)的數(shù)組。并且,定義數(shù)組時對下標運算符的數(shù)量并沒有限制,因此只要愿意就可以定義一個數(shù)組,它的元素是數(shù)組,數(shù)組的元素又是數(shù)組(禁止套娃?。?/p>
對于二維數(shù)組來說,常吧第一個維度成為行,第二個維度稱為列。
多維數(shù)組的初始化
允許使用花括號括起來的一組值初始化多維數(shù)組,這點和普通的數(shù)組一樣。下面初始化形式中,多維數(shù)組的每一行分別用花括號括了起來:
int ia[3][4]= //三個元素,每個元素都是大小為4的數(shù)組 { {0,1,2,3}, //第1行的初始值 {4,5,6,7}, //第2行的初始值 {8,9,10,11} //第3行的初始值 };
其中內層嵌套的花括號并非必需的,例如下面的初始化語句,形式上更為簡潔,完成的功能和上面的代碼完全一致:
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //沒有標識每行的花括號,與之前的初始化語句是等價的
類似一維數(shù)組,在初始化多維數(shù)組時也并非所有元素的值都必須包含在初始化表之內。如果僅僅想初始化每一行的第一個元素,通過如下的語句:
int ia[3][4] = {{0},{4},{8}}; //顯式地初始化每行的首元素
其他沒有列出的元素執(zhí)行默認初始化,這個過程和一位數(shù)組一樣。此時如果去掉內層花括號,結果就不同了。
int ix[3][4] = {0,3,6,9}; //顯式地初始化第1行,其他元素執(zhí)行值的初始化。
此時的含義是,它初始化了第一行的4個元素,其他元素被初始化0。
多維數(shù)組的下標引用
可以使用下標運算符來訪問多維數(shù)組的元素,此時數(shù)組的每個維度都對應一個下標運算符。
如果表達式含有的下標運算符數(shù)量和數(shù)組的維度一樣多,該表達式的結果將是給定類型的元素;舉個例子:arr[0][0][0]的下標運算符數(shù)量為3,并且arr是3維的,此時下標運算符數(shù)量和數(shù)組的維度就是一樣多了。
反之,如果表達式含有的下標運算符數(shù)量比數(shù)組的維度小,則表達式的結果將是定索引的一個內層數(shù)組:
ia[2][3] = arr[0][0][0]; //用arr的首元素為ia最后一行的最后一個元素賦值 int (&row)[4] = ia[1]; //把row綁定到ia的第二個4元素數(shù)組上
在第一個例子中,對于用到的兩個數(shù)組來說,表達式提供的下標運算符數(shù)量和它們各自的維度相同。在等號左側,ia[2]得到數(shù)組ia的最后一行,此時返回的是表達ia最后一行的那個一維數(shù)組而并非任何實際元素;對于這個一維數(shù)組再去取下標,得到編號為[3]的元素,也就是這行的最后一個元素。
經(jīng)常使用for循環(huán)來處理多維數(shù)組的元素,二維數(shù)組大多數(shù)就兩層嵌套的for循環(huán)去處理,外層for循環(huán)處理行,內層for循環(huán)處理列;三維數(shù)組中,最外層循環(huán)表示面,中間層表示行,最內層表示列。以此遍歷數(shù)組:
constexpr size_t rowCnt = 3 , colCnt = 4; int ia[rowCnt][colCnt]; for(size_t i = 0;i != rowCnt;++i) { for(size_t j = 0;j != colCnt;++j) { ia[i][j] = i * colCnt + j; } }
使用范圍for語句處理多維數(shù)組
由于在C++11新標準中新增了范圍for語句,所以前一個程序可以簡化為如下形式:
size_t cnt = 0; for (auto &row : ia) { for(auto &col : row) { col = cnt; ++cnt; } }
ia是一個由數(shù)組構成的數(shù)組,每次遍歷,相當于一行一行的遍歷了ia。所以每次遍歷row相當于取出一行ia。相當于:對于外層數(shù)組的每一個元素。
而col又相當于每一行的row的引用。相當于:對于內層數(shù)組的每一個元素。
這個循環(huán)賦值給ia元素的值和之前的那個循環(huán)是完全相同的,區(qū)別在于通過使用范圍for語句把管理數(shù)組索引的任務交給了系統(tǒng)。因為要改變元素的值,所以得把控制變量row和col聲明成引用類型。 第一個for循環(huán)遍歷ia的所有元素,這些元素是大小為4的數(shù)組,因此row的類型就應該是含有4個整數(shù)的數(shù)組的引用。 第二個for循環(huán)遍歷那些4個元素數(shù)組中的某一個,因此col的類型是整數(shù)的引用。每次迭代把cnt的值賦給ia的當前元素,然后將cnt加1。
在上面的例子中,因為要改變數(shù)組元素的值,所以我們選用引用類型作為循環(huán)控制變量,但是其實還有一個深層次的原因促使我們這么做。
for(const auto &row:ia) for(auto col:row) cout<<col<<endl;
這個循環(huán)中并沒有任何寫操作,但是我們還是將外層循環(huán)的控制變量聲明成了引用類型,這是為了 避免數(shù)組ia被自動轉換成指針 。
for(auto row:ia) for(auto col:row)
此時ia被自動轉化成指針,row也變成指針了,auto col:row就變成col遍歷row的每一個地址。但是我們又不要遍歷地址。所以外層的引用方式必須要加的。
程序無法通過編譯。這是因為,想之前一樣第一個循環(huán)遍歷ia的所有元素,注意這些元素實際上是大小為4的數(shù)組。因為row不是引用類型,所以編譯器初始化row時會自動將這些數(shù)組形式的元素(和其他類型的數(shù)組一樣)轉換成指向該數(shù)組內首元素的指針。這樣會得到的row的類型就是int*,顯然內層的循環(huán)就不合法了,編譯器將試圖在一個int*內遍歷,這顯然和程序的初衷不一樣。
Tips:要使用范圍for語句處理多維數(shù)組,除了最內層的循環(huán)外,其他所有循環(huán)的控制變量都應該是引用類型。
指針和多維數(shù)組
當程序使用多維數(shù)組的名字時,也會自動將其轉換成指向數(shù)組首元素的指針。
因為多維數(shù)組實際上是數(shù)組的數(shù)組,所以由多維數(shù)組名轉換而來的指針實際上是指向第一個內層數(shù)組的指針:
int ia[3][4]; //大小為3的數(shù)組,每個元素是含有4個整數(shù)的數(shù)組 int (*p)[4] = ia; //p指向含有4個整數(shù)的數(shù)組 p = &ia[2]; //p指向ia的尾元素。
(*p)意味著p是一個指針。接著看右側,指針p指向的是一個維度為4的數(shù)組;再看左側可知,數(shù)組中的元素是整數(shù)。所以,p是指向含有4個整數(shù)的數(shù)組的指針。
//在上述聲明中,圓括號必不可少: int *ip[4]; //整形指針的數(shù)組 int (*ip)[4]; //指向含有4個整數(shù)的數(shù)組
隨著C++11新標準提出,通過使用auto或者decltype就能盡可能地避免在數(shù)組前面加上一個指針類型了:
//輸出ia中每個元素的值,每個內層數(shù)組各占一行 //p指向含有4個整數(shù)的數(shù)組 for(auto p = ia;p != ia + 3; ++p) { //q指向4個整數(shù)數(shù)組的首元素,也就是說,q指向一個整數(shù) for(auto q = *p; q != *p + 4;++q) cout << *q <<' '; cout<<endl; }
外層的for循環(huán)首先聲明了一個指針并且令其指向ia的第一個內層數(shù)組,然后依次迭代直到ia的全部3行都處理完為止。其中遞增運算++p負責將指針p移動到ia的下一行。
內層的for循環(huán)負責輸出內層數(shù)組所包含的值。它首先令指針q指向p當前所在行的第一個元素。*p是一個含有4個數(shù)組的數(shù)組,像往常一樣,數(shù)組名被自動地轉換成指向該數(shù)組首元素的指針。內層for循環(huán)不斷迭代直到我們處理完了當前內層數(shù)組的所有元素為止。為了獲取內層for循環(huán)的終止條件,再一次解引用p得到指向內層數(shù)組首元素的指針,給它加上4就得到了終止條件。
當然,使用標準庫函數(shù)begin和end也能實現(xiàn)同樣的功能,而且看起來更簡潔一些:
//p指向ia的第一個數(shù)組 for(auto p = begin(ia); p != end(ia); ++p) { //q指向內層數(shù)組的首元素 for(auto q = begin(*p); q != end(*p); ++q) cout<< *q << ' ' ; //輸出q指向的整數(shù) cout <<endl; }
在此代碼中,循環(huán)終止條件由end函數(shù)負責判斷。雖然我們能夠判斷出p和q的類型,但是直接使用auto關鍵字我們就不必再操心這些類型是什么了。
類型別名簡化多維數(shù)組的指針
讀,寫和理解一個指向多維數(shù)組的指針是讓人不勝其煩的工作,使用類型別名能讓工作變得簡單一點。
using int_array = int[4]; //新標準下類型別名的聲明 typedef int int_array[4]; //等價typedef聲明 //輸出ia中每個元素的值,每個內層數(shù)組各占一行 for(int_array *p = ia; p != ia + 3;++q) { for (int *q = *p;q != *p + 4; ++q) cout<< *q <<' '; cout<<endl; }
程序將類型“4個整數(shù)組成的數(shù)組” 命名為int_array,用類型名int_array定義外層循環(huán)的控制變量讓程序看著更簡潔;
到此這篇關于C++ 多維數(shù)組詳解的文章就介紹到這了,更多相關C++ 多維數(shù)組內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C++11 std::function和std::bind 的使用示例詳解
C++11中的std::function和std::bind是函數(shù)對象的重要組成部分,它們可以用于將函數(shù)和參數(shù)綁定在一起,形成一個可調用的對象,這篇文章主要介紹了C++11 std::function和std::bind 的使用示例詳解,需要的朋友可以參考下2023-03-03C++11新特性之隨機數(shù)庫(Random?Number?Library)詳解
相對于C++11之前的隨機數(shù)生成器來說,C++11的隨機數(shù)生成器是復雜了很多,下面這篇文章主要給大家介紹了關于C++11新特性之隨機數(shù)庫(Random?Number?Library)的相關資料,需要的朋友可以參考下2022-06-06