我今天正在探索多维数组,我遇到的博客区分了矩形数组和锯齿状数组;通常我会在锯齿状和矩形上做到这一点:
Object** obj = new Obj*[5];
for (int i = 0; i < 5; ++i)
{
obj[i] = new Obj[10];
}
但是在博客中有人说,如果我知道2d数组是矩形的,那么我最好将整个事物分配到1d数组并使用临时访问元素的方法,如下所示:
Object* obj = new Obj[rows * cols];
obj[x * cols + y];
//which should have been obj[x][y] on the previous implementation
我不知何故有一个线索,即分配一个连续的内存块会很好,但我真的不明白它会产生多大的差异,有人可以解释一下吗?
答案 0 :(得分:0)
将2D数组分配为一个大块允许编译器生成比在多个块中执行更高效的代码。至少,在一个块方法中会有一个指针解引用操作。顺便说一下,像这样声明2D数组:
console application
相当于:
Object obj[rows][cols];
obj[x][y];
速度方面。但第一个不是动态的(你需要在编译时指定“rows”和“cols”的值。
答案 1 :(得分:0)
首先,不太重要,当你分配和释放你的对象时,你只需要进行一次分配/解除分配。
更重要的是:当你使用数组时,你基本上可以对内存访问进行乘法交换。在现代计算机上,内存访问比算术慢得多。
这有点谎言,因为内存访问的大部分缓慢被缓存隐藏 - 经常访问的内存区域存储在CPU内部或非常靠近CPU的快速内存中,并且可以被访问快点。但是这些缓存的大小有限,所以(1)如果你的数组没有被一直使用,那么行指针可能不在缓存中;(2)如果 正在使用所有那时候他们可能会占用其他东西可以使用的空间。
但它究竟是如何工作的将取决于代码的细节。在许多情况下,它不会以某种方式与程序的速度产生明显差异。你可以尝试两种方式和基准。
[编辑补充说,在Peter Schneider的评论提醒之后:]另外,如果你分别分配每一行,它们最终可能会在内存的不同部分,这可能会使你的缓存效率降低 - 数据以块的形式被拉入缓存中,如果你经常从一行的末尾开始到下一行的开头,那么你将从中受益。但这是一个微妙的;在某些情况下,你的行在内存中等间隔可能实际上使缓存执行更糟,如果你连续分配几行,它们很可能最终(几乎)在内存中彼此相邻,并且在任何情况下,除非你的行很短,否则它可能并不重要。
答案 2 :(得分:0)
通过拥有一个大的连续内存块,您可以获得更高的性能,因为内存访问已经更多地存在于缓存中。这个想法被称为缓存局部性。我们说大型阵列具有更好的缓存局部性。现代处理器具有多级缓存。最低级别通常是最小和最快的。
以有意义的方式访问阵列仍然是值得的。例如,如果数据以行主要顺序存储,并且您按列主要顺序访问它,则会分散您的内存访问。在某些大小,这种访问模式将否定缓存的优势。
具有良好的缓存性能远比您对索引值乘以值的任何担忧要好得多。
答案 3 :(得分:0)
如果数组的某个维度是编译时常量,则可以分配一个真正的二维数组&#34;动态地在一个块中然后以通常的方式索引它。与所有数组的动态分配一样,new
返回指向元素类型的指针。在这种二维阵列的情况下,元素依次是阵列 - 一维阵列。结果元素指针的语法有点麻烦,主要是因为解除引用operator*()
的优先级低于索引operator[]()
。一个可能的分配语句可以是int (*arr7x11)[11] = new int[7][11];
。
下面是一个完整的例子。如您所见,分配中最里面的索引可以是运行时值;它确定分配的数组中元素的数量。其他索引确定动态分配的数组的元素类型(以及因此元素大小以及整体大小),当然必须知道它们才能执行分配。如上所述,元素本身就是数组,这里是11维的1维数组。
#include<cstdio>
using namespace std;
int main(int argc, char **argv)
{
constexpr int cols = 11;
int rows = 7;
// overwrite with cmd line arg if present.
// if scanf fails, default is retained.
if(argc >= 2) { sscanf(argv[1], "%d", &rows); }
// The actual allocation of "rows" elements of
// type "array of 'cols' ints". Note the brackets
// around *arr7x11 in order to force operator
// evaluation order. arr7x11 is a pointer to array,
// not an array of pointers.
int (*arr7x11)[cols] = new int[rows][cols];
for(int row = 0; row<rows; row++)
{
for(int col = 0; col<cols; col++)
{
arr7x11[row][col] = (row+1)*1000 + col+1;
}
}
for(int row = 0; row<rows; row++)
{
for(int col = 0; col<cols; col++)
{
printf("%6d", arr7x11[row][col]);
}
putchar('\n');
}
return 0;
}
示例会话:
g++ -std=c++14 -Wall -o 2darrdecl 2darrdecl.cpp && ./2darrdecl 3
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011