首页 / 技术分享 / 正文 C++的“动态表格”:精通 `vector<vector<T>>` (二维向量) 2026-01-21 2 28 min 在C++编程中,你很快会需要处理“**二维**”数据,比如**表格**、**矩阵**或**棋盘**。 你首先想到的可能是C风格的“**固定**”二维数组: `int matrix[10][5]; // 必须在 *编译时* 就知道大小 (10x5)` **一个简单的比喻:“预制书架” vs “可扩展的文件柜”** * **C风格数组 (`int arr[10][5]`) —— “预制书架”** * 就像一个“**预制**”的书架。在“**出厂**”(**编译时**),它的“蓝图”就**写死**了“**10 层**,**每层 5 格**”。 * 你**不能**在运行时给它增加第 11 层。 * 它的**每一层**(每一行)的格子数(列数)**必须完全相同**。 * **`std::vector>` (二维向量) —— “可扩展的文件柜”** * 这是一个\*\*“动态”\*\*的“**文件柜**”。 * “外层”的 `vector` 就是“**柜子**”本身,它装着“**抽屉**”(`vector`)。 * “内层”的 `vector` 就是“**抽屉**”,它装着“**文件**”(`int`)。 * **魔力 1 (动态行):** 你可以**随时**给“柜子”**添加新的“抽屉”**(`myVec.push_back(...)`)。 * **魔力 2 (锯齿状):** 每个“抽屉”的**大小可以不同**!`drawer[0]` 可以装 3 个文件,而 `drawer[1]` 可以装 10 个文件。这被称为“**锯齿状数组**” (Jagged Array)。 `std::vector>` 是现代 C++ 中处理“动态二维数据”的**首选**方式。 **在本教程中,你将学会:** * `✅` **`vector>`**:它与C风格数组的根本区别。 * `✅` **“黄金”初始化方法**:如何**一步到位**创建一个 `Rows x Cols` 的**矩形**表格。 * `✅` **“锯齿状”数组**:如何创建“行长不一”的动态数组。 * `✅` **访问与遍历**:如何使用**嵌套 `for` 循环**来读写 `myVec[row][col]`。 * `✅` **新手的“头号噩梦”**:`v.size()` vs `v[0].size()` —— 如何**安全地**获取“行数”和“列数”。 * `✅` **“X光透视”**:用调试器“亲眼目睹”这个“柜子”和“抽屉”在内存中的结构。 **前置知识说明 (100% 自洽):** * **`vector` (向量)**:C++标准库提供的一种“动态数组”(“魔法弹性盒子列表”)。你需要 `#include ` 来使用它。 * **`push_back()`**:`vector` 的一个“工具”,用于在“**末尾**”添加一个新元素(“新乘客上车”)。 * **`.size()`**:一个附加在 `vector` 盒子上的“工具”,用来“测量”里面有多少个元素。 * **`cout`**:C++ 中用于在屏幕上打印信息的“扬声器”。 * **`for` 循环 (嵌套)**:`for` 循环内部再套一个 `for` 循环,常用于遍历二维数据。 * **编译 (Compile)**:C++代码(“食谱”)必须被“编译”(“烘焙”),才能变成电脑可执行的程序(“蛋糕”)。 --- ### **第一部分:“黄金”初始化法 —— 创建“矩形”表格** 新手最常问的问题是:“我如何创建一个 `5x10` 的 `vector`,并**全部初始化为 0**?” 你**不需要**用循环。`vector` 的构造函数(“建造工”)就可以“一步到位”。 **`grid_init.cpp` (推荐的方式)** ```cpp #include #include using namespace std; int main() { int ROWS = 3; int COLS = 4; int INITIAL_VALUE = 0; // 初始值 // --- “黄金”初始化语法 --- // 1. “建造”一个“柜子” (外层 vector),告诉它你要 *ROWS* 个“抽屉” // 2. “递给”它一个“抽屉模板” (内层 vector): // 这个“模板”是 vector(COLS, INITIAL_VALUE) // (一个 *大小为 COLS*、*全部填充为 INITIAL_VALUE* 的 vector) vector> grid(ROWS, vector(COLS, INITIAL_VALUE)); cout << "--- 3x4 矩阵 (全部为 0) ---" << endl; // --- 访问与遍历 (见下一节) --- // 我们可以修改一个值 grid[1][2] = 99; // 修改第 1 行, 第 2 列 (从 0 开始) // 使用嵌套循环打印 for (int i = 0; i < ROWS; ++i) { for (int j = 0; j < COLS; ++j) { cout << grid[i][j] << "\t"; // \t = Tab, 用于对齐 } cout << endl; // 每行结束后换行 } return 0; } ``` **“手把手”终端模拟:** ```powershell PS C:\MyCode> g++ grid_init.cpp -o grid_init.exe -std=c++11 PS C:\MyCode> .\grid_init.exe --- 3x4 矩阵 (全部为 0) --- 0 0 0 0 0 0 99 0 0 0 0 0 ``` --- ### **第二部分:“锯齿状”数组 —— 手动 `push_back`** 如果你**不**想要一个“矩形”,而是想让**每一行**的**长度都不同**呢?这时你就不能用“黄金”初始化法了,你必须“手动”`push_back` 每一个“抽屉”。 **`jagged_array.cpp`** ```cpp #include #include using namespace std; int main() { cout << "--- 创建“锯齿状”数组 ---" << endl; // 1. 创建一个 *空* 的“文件柜” vector> jaggedVec; // 2. “塞入”第一个“抽屉” (只有 3 个文件) vector row0 = {1, 2, 3}; jaggedVec.push_back(row0); // 3. “塞入”第二个“抽屉” (有 5 个文件) vector row1 = {4, 5, 6, 7, 8}; jaggedVec.push_back(row1); // 4. “塞入”第三个“抽屉” (只有 2 个文件) jaggedVec.push_back( {9, 10} ); // C++11 风格的“临时”抽屉 // --- 打印“锯齿状”数组 --- cout << "--- 遍历“锯齿状”数组 ---" << endl; for (int i = 0; i < jaggedVec.size(); ++i) { cout << "Row " << i << ": "; for (int j = 0; j < jaggedVec[i].size(); ++j) { cout << jaggedVec[i][j] << " "; } cout << endl; } return 0; } ``` **“手把手”终端模拟:** ```powershell PS C:\MyCode> g++ jagged_array.cpp -o jagged_array.exe -std=c++11 PS C:\MyCode> .\jagged_array.exe --- 创建“锯齿状”数组 --- --- 遍历“锯齿状”数组 --- Row 0: 1 2 3 Row 1: 4 5 6 7 8 Row 2: 9 10 ``` --- ### **第三部分:新手的“头号噩梦”—— `v.size()` vs `v[0].size()`** 这是新手在使用 `vector>` 时**最常**犯的错误。 **`size_trap.cpp` (陷阱演示)** ```cpp #include #include using namespace std; int main() { int ROWS = 3; int COLS = 4; vector> grid(ROWS, vector(COLS, 0)); // “行内预警”:这两个 *完全* 不同! // v.size() 问的是“文件柜” // “文件柜”里有多少个“抽屉”? cout << "grid.size() = " << grid.size() << endl; // 打印 ROWS (3) // v[0].size() 问的是“第 0 号抽屉” // “第 0 号抽屉”里有多少个“文件”? cout << "grid[0].size() = " << grid[0].size() << endl; // 打印 COLS (4) // “噩梦”:如果你在“锯齿状”数组上犯这个错... vector> jaggedVec; jaggedVec.push_back( {1, 2, 3} ); // 第 0 行,size = 3 jaggedVec.push_back( {4, 5, 6, 7, 8} ); // 第 1 行,size = 5 cout << "\n--- 锯齿状 ---" << endl; cout << "jaggedVec.size() = " << jaggedVec.size() << endl; // 打印 2 (有 2 行) cout << "jaggedVec[0].size() = " << jaggedVec[0].size() << endl; // 打印 3 cout << "jaggedVec[1].size() = " << jaggedVec[1].size() << endl; // 打印 5 return 0; } ``` **“黄金法则”:** * **`myVector.size()`** 永远等于“**行数**” (Rows)。 * **`myVector[i].size()`** 永远等于**第 `i` 行**的“**列数**” (Columns)。 * 在遍历一个\*\*“锯齿状”**数组时,内层循环的**边界\*\* *必须* 使用 `myVector[i].size()`,**绝不能**使用一个固定的 `COLS`! --- ### **第四部分:“X光透视”——亲眼目睹“柜子”和“抽屉”** 让我们用“X光眼镜”(调试器)来观察 `jagged_array.cpp` 的内存结构。 #### **“X光”实战(基于 `jagged_array.cpp`)** 1. **设置断点:** * **动作:** 在VS Code中,把你的鼠标移动到 `jagged_array.cpp` 的**第22行**(`for (int i = 0; ...)` 那一行)的**行号左边**。 * **点击**那个小 red dot,设置一个**断点**。 2. **启动“子弹时间”(F5):** * **动作:** 按下 `F5` 键。 * **你会看到:** 程序“冻结”在第22行,`jaggedVec` **已经**被 `push_back` 填满了。 3. **开启“X光”(观察 `jaggedVec`):** * **动作:** 仔细看那个“变量”(VARIABLES)窗口。 * **你会看到:** `jaggedVec: size=3, capacity=...` * **(关键!)** **展开 `jaggedVec`**(点击它旁边的“小箭头” `>`)。 * **你会看到“柜子”里的“抽屉”:** * `[0]: vector size=3, capacity=...` * `[1S]: vector size=5, capacity=...` * `[2]: vector size=2, capacity=...` * **顿悟时刻:** 调试器**完美地**向你展示了 `jaggedVec` 是一个“装 `vector` 的 `vector`”! 4. **“深入”观察(展开“抽屉”):** * **动作:** **再次展开** 第 0 个元素 `[0]: vector size=3...` * **你会看到“抽屉”里的“文件”:** * `[0]: 1` * `[1]: 2` * `[2]: 3` * **动作:** **展开** 第 1 个元素 `[1]: vector size=5...` * **你会看到:** * `[0]: 4` * `[1]: 5` * `[2]: 6` * `[3]: 7` * `[4]: 8` * **顿悟时刻:** 调试器让你**亲眼见证**了“锯齿状”结构——`[0]` 的大小确实是 3,而 `[1]` 的大小确实是 5。 --- ### **动手试试!(终极挑战:你的“乘法表”生成器)** 现在,你来当一次“数学表”的制作者。 **任务:** 1. **编写一个函数**,名叫 `createMultiplicationTable`。 2. **函数签名:** `vector> createMultiplicationTable(int n)`。 3. 在函数**内部**: * **(关键!)** 使用“**黄金初始化法**” `vector(rows, vector(cols, val))` 来**一步到位**创建一个 `n x n` 的 `vector>`,所有值**初始化为 0**。 * (注意:`n` 行 `n` 列,所以 `ROWS = n`, `COLS = n`) * **使用嵌套 `for` 循环**(`i` 从 0 到 `n-1`,`j` 从 0 到 `n-1`)来**填充**这个“表格”。 * **逻辑:** `table[i][j] = (i + 1) * (j + 1);` (因为我们要 1x1, 1x2... 而不是 0x0) * **返回**这个“表格”。 4. 在 `main` 函数中: * 提示用户输入一个大小(比如 `10`)。 * 调用 `vector> myTable = createMultiplicationTable(10);`。 * **自己**编写嵌套 `for` 循环来**打印**这个 `myTable`(提示:使用 `myTable.size()` 获取行数,`myTable[i].size()` 获取列数)。 **`multi_table.cpp` (你的 TODO):** ```cpp #include #include #include // 需要 setw 来格式化 using namespace std; // --- TODO 1-3: “工厂”函数 --- // vector> createMultiplicationTable(int n) { // // 1. “黄金”初始化 (n x n, 初始值 0) // vector> table(n, vector(n, 0)); // // 2. 嵌套循环填充 // for (int i = 0; i < n; ++i) { // 遍历 *行* // for (int j = 0; j < n; ++j) { // 遍历 *列* // // (i+1) * (j+1) // // table[i][j] = ...; // } // } // // 3. 返回 // return table; // } int main() { int size = 10; // cout << "请输入乘法表大小 (例如 10): "; // cin >> size; // --- TODO 4: 调用 --- // vector> myTable = createMultiplicationTable(size); // --- TODO 4: 打印 --- cout << "--- " << size << "x" << size << " 乘法表 ---" << endl; // (使用嵌套 for 循环打印 myTable) // for (int i = 0; i < myTable.size(); ++i) { // for (int j = 0; j < myTable[i].size(); ++j) { // cout << setw(5) << myTable[i][j]; // setw(5) 用于对齐 // } // cout << endl; // } return 0; } ``` > 这个挑战让你实践了二维 `vector` 最核心的“**初始化**”和“**遍历**”操作。完成它,你就能自如地在 C++ 中使用动态的“表格”了! > 标签:C++ 分享: ← 上一篇 《看似普通的云服务器线路,背后竟藏着这些不为人知的秘密!》 下一篇 C++的“日历”排序:精通 `std::sort` 排序自定义日期 → 相关推荐 C++的“日历”排序:精通 `std::sort` 排序自定义日期 2026-01-21 💬 评论 😊 😀 😂 😊 😍 🥰 😎 🤔 👍 👏 🎉 ❤️ 🔥 ✨ 💯 😢 🙏 💪 👀 🌟 💖 🌈 提交评论