二维数组内存连续,列数须编译期确定;函数参数需显式声明列数;std::array优于裸数组,vector<vector<T>>非连续,单vector更高效;memset仅适用于POD类型。

二维数组在内存里其实是连续的一维块
别被“二维”两个字骗了,C++ 的 int arr[3][4] 在内存中就是 12 个 int 挨着放,没有“行尾换行”这种结构。编译器靠下标计算偏移:访问 arr[i][j] 实际等价于 *((int*)arr + i * 4 + j)(假设列数是 4)。所以列数必须在编译期确定——这是最常踩的坑。
常见错误现象:error: declaration of 'arr' as multidimensional array must have bounds for all dimensions except the first。比如写 void func(int arr[][]) 直接报错,因为第二维缺失,编译器算不出每行多长。
- 函数参数里传二维数组,必须写明列数:
void process(int arr[][5], int rows) - 用指针模拟时,
int (*p)[5]是指向含 5 个int的数组的指针,不是int** -
std::vector<:vector>></:vector>看似方便,但每行内存不连续,缓存不友好,矩阵运算慢不少
用 std::array<:array n>, M> 替代裸数组更安全
如果你确定尺寸固定(比如图像像素、小规模状态矩阵),std::array 是比 C 风格数组更好的选择:它有 .size()、能传值、支持范围 for,而且不会隐式退化成指针。
使用场景:需要栈上分配、零开销抽象、又不想手写边界检查的场合,比如物理引擎里的 3×3 变换矩阵。
立即学习“C++免费学习笔记(深入)”;
- 声明:
std::array<:array>, 3> mat;</:array>—— 行优先,mat[i][j]直接可用 - 不能写成
std::array<:array>, 3></:array>,第二维大小不可省略 - 和裸数组一样,
sizeof(mat)就是全部元素所占字节,没额外开销 - 注意:它不提供行列互换视图,转置得自己循环拷贝
动态大小矩阵该用 vector<vector<T>> 还是单 vector?
运行时才知道行列数?那就得堆上分配。但 std::vector<:vector>></:vector> 是“指针数组+多个独立内存块”,而 std::vector<int></int> 加手动索引才是真正的二维连续布局。
性能影响明显:后者 CPU 缓存命中率高,适合密集数值计算;前者灵活但每次 vec[i][j] 要两次指针解引用,还可能触发多次内存分配。
- 推荐单 vector 方案:
std::vector<int> data(rows * cols);</int>,访问用data[i * cols + j] - 封装一层轻量 wrapper 类,重载
operator()实现mat(i, j)语法,避免出错 - 别用
vector<vector>> v(rows, vector<int>(cols))</int></vector>初始化——它会先构造一个临时vector,再复制 rows 次,效率低 - 如果真要分层语义(比如每行长度不同),才考虑嵌套 vector,但那就不是“矩阵”了
memset 或 memcpy 对二维数组的误用
有人想快速清零 int arr[10][10],就写 memset(arr, 0, sizeof(arr)) —— 这其实没问题,因为它是连续内存。但换成 std::vector<:vector>></:vector> 后,memset(&vec, 0, sizeof(vec)) 就灾难了:只清了外层 vector 的控制块,内部数据毫发无损,还可能破坏指针有效性。
错误现象:程序跑一会儿就 segmentation fault,或者数值突变成极大负数。
- 对裸数组或
std::array,memset安全,前提是确认类型可平凡复制(POD) - 对任何含构造函数/析构函数的类型(如
std::string成员),禁用memset - 统一用
std::fill或循环赋值更稳妥:for (auto& row : arr) std::fill(row.begin(), row.end(), 0); -
memcpy同理:只用于 POD 类型,且目标空间必须足够大、未被构造过
事情说清了就结束。真正难的不是怎么写,而是想清楚——这个“矩阵”到底是数学概念、内存布局需求,还是接口契约。三者不一致,后面全是坑。








