多維數(shù)組
1. 什么是多維數(shù)組?
在 C++ 中,嚴格來說并沒有原生的“多維數(shù)組”類型。我們所說的多維數(shù)組,實際上是“數(shù)組的數(shù)組” (Array of Arrays)。最常見的是二維數(shù)組,可以將其想象成一個表格或矩陣,包含行和列。三維數(shù)組可以看作是多個二維表格的集合,以此類推。
- 二維數(shù)組 (2D Array): 一個一維數(shù)組,其每個元素本身也是一個一維數(shù)組。
- 三維數(shù)組 (3D Array): 一個一維數(shù)組,其每個元素是一個二維數(shù)組。
- ...以此類推。
2. 聲明多維數(shù)組
聲明多維數(shù)組需要指定每個維度的大?。ǔ说谝粋€維度在某些情況下可以省略)。
-
二維數(shù)組聲明:
dataType arrayName[dimension1_size][dimension2_size];
其中dimension1_size通常代表“行數(shù)”,dimension2_size代表“列數(shù)”。int matrix[3][4]; // 聲明一個 3 行 4 列的二維整數(shù)數(shù)組 (共 3 * 4 = 12 個整數(shù)) double coordinates[10][3]; // 聲明一個包含 10 個點的三維坐標數(shù)組 (10 行, 3 列) char gameBoard[8][8]; // 聲明一個 8x8 的字符數(shù)組,可用于棋盤游戲 -
三維及更高維數(shù)組聲明:
dataType arrayName[dim1][dim2][dim3]...[dimN];float cube[5][5][5]; // 聲明一個 5x5x5 的三維浮點數(shù)數(shù)組
3. 初始化多維數(shù)組
可以使用嵌套的花括號 {} 初始化列表來初始化多維數(shù)組。
-
二維數(shù)組初始化:
-
完整初始化: 外層花括號包含代表每一行的內(nèi)層花括號。
int matrix[2][3] = { {1, 2, 3}, // 第一行 (matrix[0]) 的元素 {4, 5, 6} // 第二行 (matrix[1]) 的元素 }; -
部分初始化: 如果提供的初始值不足,剩余的元素會被自動零初始化。
int matrix[3][4] = { {1, 2}, // 初始化為 {1, 2, 0, 0} {3}, // 初始化為 {3, 0, 0, 0} // 第三行未指定,初始化為 {0, 0, 0, 0} }; // 結果: {{1, 2, 0, 0}, {3, 0, 0, 0}, {0, 0, 0, 0}} -
省略第一維度大?。?/strong> 如果提供了完整的初始化列表,可以省略第一個維度的大小,編譯器會自動推斷。
int matrix[][3] = { // 編譯器推斷第一維大小為 2 {1, 2, 3}, {4, 5, 6} }; -
扁平化初始化 (不推薦,可讀性差): 可以省略內(nèi)層花括號,按行主序依次提供所有元素的值。
int matrix[2][3] = {1, 2, 3, 4, 5, 6}; // 等同于上面的嵌套初始化
-
完整初始化: 外層花括號包含代表每一行的內(nèi)層花括號。
4. 內(nèi)存布局:行主序 (Row-Major Order)
這是理解多維數(shù)組的關鍵。盡管我們將其想象為二維或三維結構,但在內(nèi)存中,C++ (以及 C 和許多其他語言) 將多維數(shù)組存儲為一維的連續(xù)數(shù)據(jù)塊。存儲順序遵循行主序,意味著最后一個索引變化最快。
對于二維數(shù)組 int matrix[R][C];:
- 內(nèi)存布局:
matrix[0][0], matrix[0][1], ..., matrix[0][C-1], matrix[1][0], matrix[1][1], ..., matrix[1][C-1], ..., matrix[R-1][0], ..., matrix[R-1][C-1] - 所有元素(
R * C個)連續(xù)存放。第一行的所有元素先存放,然后是第二行的所有元素,依此類推。
示例: int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
內(nèi)存中的順序是: 1, 2, 3, 4, 5, 6
5. 訪問多維數(shù)組元素
使用多個方括號 [] 和對應的索引來訪問元素。索引仍然是從 0 開始。
int matrix[3][4];
matrix[0][0] = 10; // 訪問第一行第一列的元素
matrix[1][2] = 25; // 訪問第二行第三列的元素
int value = matrix[1][2]; // 讀取第二行第三列的值 (25)
// 訪問越界同樣會導致未定義行為!
// matrix[3][0] 是越界的 (行索引最大為 2)
// matrix[0][4] 是越界的 (列索引最大為 3)
編譯器利用行主序布局和維度大小來計算元素在內(nèi)存中的實際地址:
對于 matrix[r][c],其地址約為 base_address + (r * NumberOfColumns + c) * sizeof(elementType)。這就是為什么在函數(shù)參數(shù)中通常需要指定除第一維之外的所有維度大小——編譯器需要這些信息來正確計算地址。
6. 將多維數(shù)組傳遞給函數(shù)
有幾種常見的方法:
-
指定所有維度:
void processMatrix(int m[3][4]) { m[1][1] = 0; } // 缺點:函數(shù)只能處理固定大小 (3x4) 的數(shù)組 -
省略第一維度(常用):
void processMatrix(int m[][4], int rows) { // 必須指定除第一維外的所有維度大小 for (int i = 0; i < rows; ++i) { for (int j = 0; j < 4; ++j) { // 列數(shù) 4 是固定的 m[i][j] *= 2; } } } int myMatrix[5][4]; processMatrix(myMatrix, 5); // 可以處理行數(shù)不同,但列數(shù)必須是 4 的數(shù)組 -
使用指向數(shù)組的指針(與上一種等價):
void processMatrix(int (*m)[4], int rows) { // m 是一個指向包含 4 個 int 的數(shù)組的指針 // ... 函數(shù)體同上 ... } -
使用模板(更通用):
template <size_t ROWS, size_t COLS> void processMatrix(int (&m)[ROWS][COLS]) { // 傳遞數(shù)組的引用 m[ROWS / 2][COLS / 2] = 99; } int myMatrix[10][20]; processMatrix(myMatrix); // 可以處理任意大小的二維數(shù)組 -
使用
std::vector<std::vector<T>>(動態(tài)大?。?/strong>
雖然這不是一個真正的、內(nèi)存連續(xù)的多維數(shù)組(它是一個 vector,其元素是其他 vector),但它提供了動態(tài)調(diào)整大小的靈活性。#include <vector> void processDynamicMatrix(std::vector<std::vector<int>>& matrix) { if (!matrix.empty() && !matrix[0].empty()) { matrix[0][0] = -1; } }
7. 常見用途
- 矩陣運算: 線性代數(shù)、科學計算。
- 圖像表示: 二維數(shù)組存儲像素數(shù)據(jù)(顏色、灰度值)。
- 游戲棋盤: 如國際象棋、圍棋、掃雷等。
- 表格數(shù)據(jù): 存儲行和列結構的數(shù)據(jù)。
- 三維模型或空間: 在圖形學或物理模擬中使用三維數(shù)組。
總結: C++ 的多維數(shù)組本質(zhì)上是“數(shù)組的數(shù)組”,在內(nèi)存中按行主序連續(xù)存儲。通過多個索引訪問元素,初始化使用嵌套花括號。傳遞給函數(shù)時通常需要指定除第一維之外的大小。理解其內(nèi)存布局對于高效使用和避免錯誤至關重要。
回顧一下:一維數(shù)組
記得嗎?一維數(shù)組就像一排緊挨著的巧克力塊:
[塊1] [塊2] [塊3] [塊4]
你只需要說“第幾個”(比如第 2 塊,編號從 0 開始就是 [1])就能找到它。
現(xiàn)在,二維數(shù)組 (2D Array):
想象一下,你拿到了一整板大巧克力!這塊大巧克力不是只有一排,而是有好幾排 (Rows),每一排又有好幾塊 (Columns)。它看起來像一個格子!
(列 Col 0) (列 Col 1) (列 Col 2)
排 Row 0: [ R0,C0 ] [ R0,C1 ] [ R0,C2 ]
排 Row 1: [ R1,C0 ] [ R1,C1 ] [ R1,C2 ]
排 Row 2: [ R2,C0 ] [ R2,C1 ] [ R2,C2 ]
排 Row 3: [ R3,C0 ] [ R3,C1 ] [ R3,C2 ]
這塊大巧克力板就像電腦里的二維數(shù)組 (二維數(shù)組)。
-
怎么找到特定一塊巧克力? 現(xiàn)在只說“第幾塊”就不夠了。你需要說清楚是“第幾排的第幾塊”。比如,“第 2 排(編號是 1)的那一排里的第 3 塊(編號是 2)”。你需要用兩個數(shù)字來定位!
-
巧克力板[排號][塊號](Chocolate[row_number][column_number]) ->巧克力板[1][2]
-
或者,想想電影院的座位:
電影院里也不是只有一排座位,對吧?有很多排,每一排有很多個座位號。你要找到你的座位,票上會寫著“幾排幾座”,比如“8 排 15 座”。這也像一個二維數(shù)組!
-
電影院[排號][座位號](Theater[row_number][seat_number])
更進一步:三維數(shù)組 (3D Array):
現(xiàn)在想象一下,你有好幾板一模一樣的大巧克力疊在一起!或者一個多層電影院,有好幾個廳(或者樓層),每個廳里都有排和座。
要找到特定的一塊巧克力或座位,你需要三個數(shù)字了:
- “第幾板巧克力的、第幾排的、第幾塊” (
巧克力[板號][排號][塊號]) - “第幾層樓的、第幾排的、第幾座” (
多層影院[樓層號][排號][座位號])
這就是三維數(shù)組 (三維數(shù)組)!
重點: 多維數(shù)組就像把我們的“小盒子”按照格子(二維)或者立方體(三維)的形狀排列起來,你需要用多個數(shù)字(下標) 才能準確找到其中的一個“小盒子”。
畫一維數(shù)組 (一排巧克力):
[ ] [ ] [ ] [ ](旁邊寫:一維數(shù)組,像一排)-
畫二維數(shù)組 (巧克力板):
- 畫一個矩形網(wǎng)格。
- 標注行號 (0, 1, 2...) 和列號 (0, 1, 2...)。
- 在格子里寫上
[行,列],比如[0,0],[0,1],[1,0],[1,1]... - 旁邊寫上:“二維數(shù)組,像一個格子/表格!”
- 用箭頭指向一個格子,例如
[1][2],并標注:“需要 2 個數(shù)字 找到它:第 1 排,第 2 列”。
列0 列1 列2 行0 [0,0][0,1][0,2] 行1 [1,0][1,1][1,2] <--- 指向 [1,2] 并說明 行2 [2,0][2,1][2,2] -
畫三維數(shù)組 (疊起來的巧克力板 - 可選簡化):
- 畫幾個稍微錯開的二維網(wǎng)格,表示疊在一起。
- 標注板號 (0, 1...),以及每個板上的行號和列號。
- 旁邊寫上:“三維數(shù)組,像疊起來的格子!”
- 用箭頭指向其中一個格子,例如
板[0] 的 [1][2],標注:“需要 3 個數(shù)字 找到它:第 0 板,第 1 排,第 2 列”。
看圖理解: 圖畫能很清楚地顯示出從一排變成一個平面格子,再變成一個立體結構。并且能看到找到一個格子需要多少個數(shù)字信息。
現(xiàn)在我們總結一下電腦是怎么理解這些“格子隊伍”的:
-
多維數(shù)組 (
多維數(shù)組):就是電腦里一種更高級的“小盒子隊伍”組織方式,不只是一條線,而是像一個平面網(wǎng)格(二維數(shù)組二維數(shù)組) 或者一個立體空間(三維數(shù)組三維數(shù)組)。 - 怎么用? 當我們需要存儲像表格、棋盤、圖片像素點(圖片就是很多行很多列的顏色點組成的嘛?。┻@類有“行”和“列”結構的數(shù)據(jù)時,用二維數(shù)組就特別方便。
-
找盒子(訪問元素): 要從多維數(shù)組里找到一個特定的“小盒子”,你需要提供多個“地址”數(shù)字(下標 Indices)。二維數(shù)組需要兩個(行號,列號),三維需要三個,以此類推。
myGrid[row][column] - 電腦的小秘密(內(nèi)存存儲): 雖然我們把二維數(shù)組想象成一個格子,但在電腦的“儲藏室”(內(nèi)存)里,它還是會很聰明地把所有小盒子拉平成一條長長的線來存放!它會先把第一排的所有盒子放好,緊接著放第二排的所有盒子,再放第三排……(這叫做“行主序 Row-major order”)。
-
為什么要拉平? 因為電腦最擅長處理一條線的隊伍!這樣它還是能用它那個快速計算地址的“魔法”(
基地址 + 行號 * 每行的盒子數(shù) + 列號),很快地找到你想要的那個盒子,即使它是在一個“格子”里!
記住: 多維數(shù)組幫助我們用電腦表示和處理像表格、棋盤、圖片這樣的“格子”信息。我們用多個下標數(shù)字來找到格子里的某個位置,而電腦內(nèi)部則聰明地把它們變成一條線來快速存儲和查找。