一、传统“指针的指针”写法(行不连续)
适用场景:行列都在运行期确定,且每行长度可不同(锯齿数组)。
C写法
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int rows = 3, cols = 4;
/* 1. 先给行指针数组分配空间 */
int **arr = (int **)malloc(rows * sizeof(int *));
if (!arr) { perror("malloc rows"); return 1; }
/* 2. 再给每一行分配列空间 */
for (int i = 0; i < rows; ++i) {
arr[i] = (int *)malloc(cols * sizeof(int));
if (!arr[i]) { perror("malloc cols"); return 1; }
}
/* 3. 使用:arr[i][j] = ... */
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
arr[i][j] = i * cols + j;
/* 4. 打印验证 */
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j)
printf("%2d ", arr[i][j]);
putchar('\n');
}
/* 5. 释放:先逐行,再释放行指针 */
for (int i = 0; i < rows; ++i)
free(arr[i]);
free(arr);
return 0;
}
cpp写法
int rows = 3, cols = 4;
// 1. 先分配行指针数组
int** arr = new int*[rows];
// 2. 再给每一行分配列空间
for (int i = 0; i < rows; ++i)
arr[i] = new int[cols];
// 3. 使用,赋值
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
arr[i][j] = i * cols + j;
/* 4. 打印验证 */
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j)
printf("%2d ", arr[i][j]);
putchar('\n');
}
// 4. 释放:先逐行,再释放行指针
for (int i = 0; i < rows; ++i)
delete[] arr[i];
delete[] arr;
#include <vector>
#include <iostream>
int main() {
// 3 行 4 列,全部初始化为 0
int rows = 3;
int cols = 4;
std::vector<std::vector<int>> arr(rows, std::vector<int>(cols, 0));
// 使用,赋值
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
arr[i][j] = i * cols + j;
/* 4. 打印验证 */
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j)
printf("%2d ", arr[i][j]);
putchar('\n');
}
}
内存示意图:
缺点:
行与行之间内存不连续,缓存命中率低。
需要多个 new,释放顺序易出错。
二、一次性分配连续内存(推荐)
适用场景:行列已知,需要整块连续内存,效率高,易与 C API 交互。
c写法
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int rows = 3, cols = 4;
/* 1. 一次性申请所有元素 + 行指针数组 */
int **arr = (int **)malloc(rows * sizeof(int *));
int *data = (int *)malloc(rows * cols * sizeof(int));
/* 2. 让行指针指向连续块的正确偏移 */
for (int i = 0; i < rows; ++i)
arr[i] = data + i * cols; // 关键一步
/* 3. 使用与赋值 */
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
arr[i][j] = i * cols + j;
/* 4. 打印 */
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j)
printf("%2d ", arr[i][j]);
putchar('\n');
}
/* 5. 释放:只需两步,顺序随意 */
free(data);
free(arr);
return 0;
}
cpp写法
#include <iostream>
int main() {
int rows = 3;
int cols = 4;
// 1. 一块连续内存存全部元素
int** arr = new int*[rows];
int* flat = new int[rows * cols];
/* 2. 让行指针指向连续块的正确偏移 */
for (int i = 0; i < rows; i++)
arr[i] = flat + i * cols;
// 3. 像二维数组一样用
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
arr[i][j] = i * cols+ j;
// 4. 打印验证
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j)
std::cout << arr[i][j] << ' ';
std::cout << '\n';
}
// 5. 释放:先指针数组,再数据块
delete[] arr;
delete[] flat;
return 0;
}
内存示意图:
data -> [0][1][2][3][4][5][6][7][8][9][10][11] // 连续
优点:
只有一段内存,缓存友好,易 delete[] data 一步释放。
三、极简“扁平化”写法(无二级指针)
如果你不需要 arr[i][j] 语法,可直接用一维指针模拟二维访问:
int rows = 3, cols = 4;
int *a = (int *)malloc(rows * cols * sizeof(int));
a[i * cols + j] = value; // 访问元素
free(a);