數據結構:二維數組(2D Arrays)

目錄

什么是二維數組?

二維數組的聲明方式

方式 1:靜態二維數組?

方式 2:數組指針數組(數組中存放的是指針)

方式 3:雙指針 + 二級堆分配

💡 補充建議

如何用“第一性原理”去推導出 C++ 中二維數組的三種聲明方式?

第一階段:內存連續,列固定,行固定 → 推導出方式①

第二階段:每行獨立、列可能不同(不規則矩陣) → 推導出方式②

第三階段:行數和列數都是運行時才知道的 → 推導出方式③


什么是二維數組?

二維數組本質上是“數組的數組”,你可以將它看作一個矩陣,每個元素有兩個索引:

int A[3][4];

表示一個包含 3 行 4 列 的整數矩陣。

訪問方式是 A[row][col],例如 A[1][2] 表示第 2 行第 3 列。

二維數組的聲明方式

我們來逐一講解,并圖示結構 + 內存布局圖解:

方式 1:靜態二維數組?

int A[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10,11,12}
};

結構理解:

這是一個連續內存塊,編譯器分配的實際大小為 3 * 4 * sizeof(int),總共 12 個整型元素。

圖示結構:?

A:     → 3 行 × 4 列的矩陣(行優先排列)[1, 2, 3, 4][5, 6, 7, 8][9,10,11,12]

?💾 內存模型(連續存儲):

+----+----+----+----+----+----+----+----+----+----+----+----+ ← 低地址
|  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 | 11 | 12 |
+----+----+----+----+----+----+----+----+----+----+----+----+ ← 高地址

按行順序(row-major)存儲:第一行 → 第二行 → 第三行

特點:

屬性說明
內存連續
高效訪問
易傳給函數是(需行數、列數信息)
可初始化所有值
靈活性(行數/列數變動)否 不可動態變長
  • 簡單高效

  • 編譯器在編譯時就知道每個元素地址的偏移

  • 適用于尺寸固定的表格/矩陣處理

優勢體現:性能最佳(連續內存 + 快速索引)

📌 示例:加速矩陣乘法(靠緩存命中率)

int A[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int B[4][2] = {{1,2}, {1,2}, {1,2}, {1,2}};
int C[3][2] = {0};for (int i = 0; i < 3; ++i)for (int j = 0; j < 2; ++j)for (int k = 0; k < 4; ++k)C[i][j] += A[i][k] * B[k][j];

優勢體現點:

  • 編譯器知道 A[i][k]B[k][j] 在內存中的確切偏移

  • 數據連續,CPU 緩存命中率高

  • 運行速度遠優于動態結構(如 vector<vector<int>>)


方式 2:數組指針數組(數組中存放的是指針)

int* A[3];           // 數組中存放3個 int* 指針
A[0] = new int[4];
A[1] = new int[4];
A[2] = new int[4];

結構理解:

  • A 是一個大小為 3 的指針數組:A[0], A[1], A[2]

  • 每個 A[i] 是一個指向 長度為 4 的數組 的指針

圖示結構:

A:      → [指針]    [指針]     [指針]↓         ↓          ↓[1,2,3,4] [5,6,7,8] [9,10,11,12]

?💾 內存模型(不連續):

棧:
+-----+-----+-----+ ← A[0], A[1], A[2] 是在棧區的指針變量
| ptr | ptr | ptr |
+-----+-----+-----+↓      ↓      ↓
堆:[1][2][3][4]       // A[0] 指向此處[5][6][7][8]       // A[1][9][10][11][12]    // A[2]

特點:

屬性說明
內存連續??否? ?每一行在堆中獨立分配
指針存儲在哪里??是? ?指針數組本身在棧
靈活性??是? ?可以變行數或列數
缺點?否? 不利于整體拷貝、效率較低、復雜性高
  • 比方式①更靈活

  • 每一行都可以單獨分配、單獨修改、單獨釋放

  • 行長度可以不同(“不規則二維數組”)

優勢體現:行長不一致的表格處理

📌 示例:一個表格,有 3 行,每行元素個數不同

int* A[3];
A[0] = new int[2]{1,2};          // 第一行只有2個元素
A[1] = new int[3]{3,4,5};        // 第二行3個
A[2] = new int[4]{6,7,8,9};      // 第三行4個for (int i = 0; i < 2; ++i) cout << A[0][i] << " ";
for (int i = 0; i < 3; ++i) cout << A[1][i] << " ";
for (int i = 0; i < 4; ++i) cout << A[2][i] << " ";delete[] A[0]; delete[] A[1]; delete[] A[2];

優勢體現點:

  • 不規則矩陣結構(每行不一樣)

  • 適合表示數據表格(例如 JSON 表、數據庫行)


方式 3:雙指針 + 二級堆分配

int** A;
A = new int*[3];         // 分配3個指針
A[0] = new int[4];
A[1] = new int[4];
A[2] = new int[4];

結構理解:

  • A 是一個指向指針的指針:int**

  • 它指向一個動態分配的指針數組

  • 每個指針再指向各自的行數組

圖示結構:

堆1(A):
+-----+-----+-----+
| ptr | ptr | ptr | ← A[0], A[1], A[2]
+-----+-----+-----+↓      ↓      ↓
堆2:[1,2,3,4] [5,6,7,8] [9,10,11,12]

?💾 內存模型(全堆 + 不連續):

棧:
+----------------+ ← A(int** 類型,指向堆中指針數組)
| 指向堆的指針地址 |
+----------------+堆(第一次 new):
+-----+-----+-----+
| ptr | ptr | ptr | ← 存放 A[0], A[1], A[2]
+-----+-----+-----+堆(3次 new):
[1,2,3,4]     ← A[0]
[5,6,7,8]     ← A[1]
[9,10,11,12]  ← A[2]

特點:

屬性說明
全部在堆中?(適合大型數據)
是否連續??不連續,多次分配
靈活性??最高,可以動態控制行/列數
管理復雜度??需手動 delete 兩層,容易泄漏
  • 最具通用性

  • 完全動態二維數組

  • 行數、列數在運行時可變,甚至可以動態增刪行列

優勢體現:完全運行時控制二維矩陣大小

📌 示例:用戶輸入決定行列數

int rows, cols;
cin >> rows >> cols;int** A = new int*[rows];
for (int i = 0; i < rows; ++i)A[i] = new int[cols];     // 每行動態開辟// 初始化矩陣
for (int i = 0; i < rows; ++i)for (int j = 0; j < cols; ++j)A[i][j] = i * cols + j;// 打印矩陣
for (int i = 0; i < rows; ++i) {for (int j = 0; j < cols; ++j)cout << A[i][j] << " ";cout << endl;
}// 釋放內存
for (int i = 0; i < rows; ++i) delete[] A[i];
delete[] A;

優勢體現點:

  • 非常適合處理用戶輸入、數據文件、圖結構(鄰接表)

  • 最接近“二維 vector”的行為

💡 補充建議

  • 如果你只需要 固定結構,首選 int A[3][4]

  • 如果你需要 運行時決定行列,用 int** Avector<vector<int>>

  • 如果你想用更現代 C++ 寫法:推薦使用 std::vector 來替代動態數組


如何用“第一性原理”去推導出 C++ 中二維數組的三種聲明方式?

基礎事實(不依賴高層語法):

  1. 內存是線性地址空間(一條一維線)

  2. 數組是連續的同類型內存塊

  3. 指針是變量的地址

  4. 一個數組可以存放指針

  5. new / malloc 可以在運行時分配任意大小的內存

  6. C++ 編譯器可以通過 arr[i][j] 翻譯成偏移運算:*( *(arr + i) + j )

🔍 我的問題目標:我想建一個二維表格

輸入需求:

  • 我希望能訪問 A[i][j]

  • 這個結構要能容納多行多列的數據

第一階段:內存連續,列固定,行固定 → 推導出方式①

?設計需求:

  • 所有元素個數是 M × N

  • 所有行列大小都是固定的

  • 適合做 數值型矩陣處理、圖像、表格

🤔 思考過程:

? 我能不能一次申請一個大數組,把所有數據按行拼在一起?

當然可以,我做個拍扁的線性數組:

int data[M * N];

然后我寫一個訪問函數:

int get(int i, int j) {return data[i * N + j];
}

👉 很方便,但語法不直觀。我希望寫 A[i][j] 而不是 data[i * N + j]

于是我想:

int A[M][N];

這不就是把 M*N 連續空間組織成二維格式了嗎?

A[i][j] 被編譯器翻譯為:

*(A + i*N + j) = *( *(A + i) + j )

這就推導出了:

?方式①:int A[3][4]


第二階段:每行獨立、列可能不同(不規則矩陣) → 推導出方式②

新設計需求:

  • 每行的長度不同

  • 不想為每一行都分配一樣多空間(內存浪費)

  • 比如:

Row 0: 3 elements
Row 1: 5 elements
Row 2: 2 elements

思考過程:

? 我能否為每一行單獨分配一個一維數組?

當然可以,我可以寫:

int* row0 = new int[3];
int* row1 = new int[5];
int* row2 = new int[2];

那我怎么管理這些行?

💡 我想到了:把這些指針放進一個數組中!

int* A[3];      // A[0], A[1], A[2] 分別指向三行
A[0] = row0;
A[1] = row1;
A[2] = row2;

現在我就可以訪問 A[i][j] 了:

  • A[i] 是一行指針

  • A[i][j] 是訪問這一行中的元素

這就推導出了:

?方式②:int* A[3];


第三階段:行數和列數都是運行時才知道的 → 推導出方式③

?新設計需求:

  • 用戶在運行時告訴我有多少行和列

  • 比如:

cin >> rows >> cols;

? 我該怎么在運行時分配二維表格呢?

我不能用 int A[rows][cols],因為在 C++ 中數組大小必須是編譯時常量(除 VLA 編譯器擴展)

🤔 思考過程:

第一步:

我要創建一個 行指針數組:

int** A = new int*[rows];

A 就是一個“二維數組的外殼”:A[i] 將是每一行的指針

第二步:

為每一行動態創建列空間:

for (int i = 0; i < rows; ++i)A[i] = new int[cols];

這樣就構成了一個“指針的指針”:二維結構。

得到:

?方式③:int** A; A = new int*[rows]; A[i] = new int[cols];

總結:需求驅動 + 構造能力 → 推導出三種方式

場景/需求你能用的構造推導出的結構實際語法
數據量固定、行列固定數組連續二維數組int A[M][N]
行數固定、列數可變數組 + 指針指針數組 + 行數組int* A[M];
行列都可變雙層指針 + 堆分配二級動態數組int** A;
目標:二維數據結構↓
選擇1:一維內存線? → 折疊二維到一維 → A[i][j] ≈ A[i*N + j] → ? int A[M][N]↓
選擇2:能不能每行分開存? → 每行是 int*,用指針數組存行 → ? int* A[M]↓
選擇3:連行數都未知? → 指針數組也要動態分配 → ? int** A; A = new int*[M]

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87533.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87533.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87533.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HAProxy 和 Nginx的區別

HAProxy 和 Nginx 都是優秀的負載均衡工具&#xff0c;但它們在設計目標、適用場景和功能特性上有顯著區別。以下是兩者的詳細對比&#xff1a;1. 核心定位特性HAProxyNginx主要角色專業的負載均衡器/代理Web 服務器 反向代理/負載均衡設計初衷高性能流量分發高并發 HTTP 服務…

基于Java+SpringBoot的健身房管理系統

源碼編號&#xff1a;S586源碼名稱&#xff1a;基于SpringBoot的健身房管理系統用戶類型&#xff1a;多角色&#xff0c;用戶、教練、管理員數據庫表數量&#xff1a;13 張表主要技術&#xff1a;Java、Vue、ElementUl 、SpringBoot、Maven運行環境&#xff1a;Windows/Mac、JD…

【MySQL安裝-yum/手動安裝,卸載,問題排查處理完整文檔(linux)】

一.使用Yum倉庫自動安裝 步驟1:添加MySQL Yum倉庫 sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-6.noarch.rpm步驟2:安裝MySQL服務器 sudo yum install mysql-server -y步驟3:啟動并設置開機自啟 sudo systemctl start mysqld sudo systemct…

自定義線程池-實現任務0丟失的處理策略

設計一個線程池&#xff0c;要求如下&#xff1a;隊列最大容量為10&#xff08;內存隊列&#xff09;。當隊列滿了之后&#xff0c;拒絕策略將新的任務寫入數據庫。從隊列中取任務時&#xff0c;若該隊列為空&#xff0c;能夠從數據庫中加載之前被拒絕的任務模擬數據庫 (TaskDa…

【NLP入門系列四】評論文本分類入門案例

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 博主簡介&#xff1a;努力學習的22級本科生一枚 &#x1f31f;?&#xff1b;探索AI算法&#xff0c;C&#xff0c;go語言的世界&#xff1b;在迷茫中尋找光芒…

Ubuntu安裝ClickHouse

注&#xff1a;本文章的ubuntu的版本為&#xff1a;ubuntu-20.04.6-live-server-amd64。 Ubuntu&#xff08;在線版&#xff09; 更新軟件源 sudo apt-get update 安裝apt-transport-https 允許apt工具通過https協議下載軟件包。 sudo apt-get install apt-transport-htt…

C++26 下一代C++標準

C++26 將是繼 C++23 之后的下一個 C++ 標準。這個新標準對 C++ 進行了重大改進,很可能像 C++98、C++11 或 C++20 那樣具有劃時代的意義。 一:C++標準回顧 C++ 已經有 40 多年的歷史了。過去這些年里發生了什么?這里給出一個簡化版的答案,直到即將到來的 C++26。 1. C++9…

【MySQL】十六,MySQL窗口函數

在 MySQL 8.0 及以后版本中&#xff0c;窗口函數&#xff08;Window Functions&#xff09;為數據分析和處理提供了強大的工具。窗口函數允許在查詢結果集上執行計算&#xff0c;而不必使用子查詢或連接&#xff0c;這使得某些類型的計算更加高效和簡潔。 語法結構 function_…

微型氣象儀在城市環境的應用

微型氣象儀憑借其體積小、成本低、部署靈活、數據實時性強等特點&#xff0c;在城市環境中得到廣泛應用&#xff0c;能夠為城市規劃、環境管理、公共安全、居民生活等領域提供精細化氣象數據支持。一、核心應用場景1. 城市微氣候監測與優化熱島效應研究場景&#xff1a;在城市不…

【仿muduo庫實現并發服務器】eventloop模塊

仿muduo庫實現并發服務器一.eventloop模塊1.成員變量std::thread::id _thread_id;//線程IDPoller _poll;int _event_fd;std::vector<Function<Function>> _task;TimerWheel _timer_wheel2.EventLoop構造3.針對eventfd的操作4.針對poller的操作5.針對threadID的操作…

Redis 加鎖、解鎖

Redis 加鎖和解鎖的應用 上代碼 應用調用示例 RedisLockEntity lockEntityYlb RedisLockEntity.builder().lockKey(TradeConstants.HP_APP_AMOUNT_LOCK_PREFIX appUser.getAccount()).value(orderId).build();boolean isLockedYlb false;try {if (redisLock.tryLock(lockE…

在 Windows 上為 WSL 增加 root 賬號密碼并通過 Shell 工具連接

1. 為 WSL 設置 root 用戶密碼 在 Windows 上使用 WSL&#xff08;Windows Subsystem for Linux&#xff09;時&#xff0c;默認情況下并沒有啟用 root 賬號的密碼。為了通過 SSH 或其他工具以 root 身份連接到 WSL&#xff0c;我們需要為 root 用戶設置密碼。 設置 root 密碼步…

2730、找到最長的半重復子字符穿

題目&#xff1a; 解答&#xff1a; 窗口為[left&#xff0c;right]&#xff0c;ans為窗口長度&#xff0c;same為子串長度&#xff0c;窗口滿足題設條件&#xff0c;即只含一個連續重復字符&#xff0c;則更新ans&#xff0c;否則從左邊開始一直彈出&#xff0c;直到滿足條件…

MCP Java SDK源碼分析

MCP Java SDK源碼分析 一、引言 在當今人工智能飛速發展的時代&#xff0c;大型語言模型&#xff08;LLMs&#xff09;如GPT - 4、Claude等展現出了強大的語言理解和生成能力。然而&#xff0c;這些模型面臨著一個核心限制&#xff0c;即無法直接訪問外部世界的數據和工具。M…

[Linux]內核如何對信號進行捕捉

要理解Linux中內核如何對信號進行捕捉&#xff0c;我們需要很多前置知識的理解&#xff1a; 內核態和用戶態的區別CPU指令集權限內核態和用戶態之間的切換 由于文章的側重點不同&#xff0c;上面這些知識我會在這篇文章盡量詳細提及&#xff0c;更詳細內容還得請大家查看這篇…

設計模式-觀察者模式、命令模式

觀察者模式Observer&#xff08;觀察者&#xff09;—對象行為型模式定義&#xff1a;定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一主題對象,在它的狀態發生變化時,會通知所有的觀察者.先將 Observer A B C 注冊到 Observable &#xff0c;那么當 Observable 狀態…

【Unity筆記01】基于單例模式的簡單UI框架

單例模式的UIManagerusing System.Collections; using System.Collections.Generic; using UnityEngine;public class UIManager {private static UIManager _instance;public Dictionary<string, string> pathDict;public Dictionary<string, GameObject> prefab…

深入解析 OPC UA:工業自動化與物聯網的關鍵技術

在當今快速發展的工業自動化和物聯網&#xff08;IoT&#xff09;領域&#xff0c;數據的無縫交換和集成變得至關重要。OPC UA&#xff08;Open Platform Communications Unified Architecture&#xff09;作為一種開放的、跨平臺的工業通信協議&#xff0c;正在成為這一領域的…

MCP 協議的未來發展趨勢與學習路徑

MCP 協議的未來發展趨勢 6.1 MCP 技術演進與更新 MCP 協議正在快速發展&#xff0c;不斷引入新的功能和改進。根據 2025 年 3 月 26 日發布的協議規范&#xff0c;MCP 的最新版本已經引入了多項重要更新&#xff1a; 1.HTTP Transport 正式轉正&#xff1a;引入 Streamable …

硬件嵌入式學習路線大總結(一):C語言與linux。內功心法——從入門到精通,徹底打通你的任督二脈!

嵌入式工程師學習路線大總結&#xff08;一&#xff09; 引言&#xff1a;C語言——嵌入式領域的“屠龍寶刀”&#xff01; 兄弟們&#xff0c;如果你想在嵌入式領域闖出一片天地&#xff0c;C語言就是你手里那把最鋒利的“屠龍寶刀”&#xff01;它不像Python那樣優雅&#xf…