C ++中的整数的三维数组
我想找出使用指针算术/动态内存分配或者使用诸如矢量之类的STL技术来在C ++中实现整数三维数组的安全方法。
本质上,我希望整数数组的尺寸看起来像:
[ x ][ y ][ z ]
x和y的范围是20-6000
z是已知的,等于4.
解决方案
回答
看一下Boost多维数组库。这是一个示例(改编自Boost文档):
#include "boost/multi_array.hpp" int main() { // Create a 3D array that is 20 x 30 x 4 int x = 20; int y = 30; int z = 4; typedef boost::multi_array<int, 3> array_type; typedef array_type::index index; array_type my_array(boost::extents[x][y][z]); // Assign values to the elements int values = 0; for (index i = 0; i != x; ++i) { for (index j = 0; j != y; ++j) { for (index k = 0; k != z; ++k) { my_array[i][j][k] = values++; } } } }
回答
每对方括号都是解引用操作(应用于指针时)。例如,以下几对代码行是等效的:
x = myArray[4]; x = *(myArray+4);
x = myArray[2][7]; x = *((*(myArray+2))+7);
要使用建议的语法,我们只需对从第一个取消引用返回的值进行取消引用。
int*** myArray = (some allocation method, keep reading); // // All in one line: int value = myArray[x][y][z]; // // Separated to multiple steps: int** deref1 = myArray[x]; int* deref2 = deref1[y]; int value = deref2[z];
要分配此数组,我们只需要认识到实际上没有三维整数数组即可。我们有一个整数数组的数组。
// Start by allocating an array for array of arrays int*** myArray = new int**[X_MAXIMUM]; // Allocate an array for each element of the first array for(int x = 0; x < X_MAXIMUM; ++x) { myArray[x] = new int*[Y_MAXIMUM]; // Allocate an array of integers for each element of this array for(int y = 0; y < Y_MAXIMUM; ++y) { myArray[x][y] = new int[Z_MAXIMUM]; // Specify an initial value (if desired) for(int z = 0; z < Z_MAXIMUM; ++z) { myArray[x][y][z] = -1; } } }
取消分配此数组与分配它类似的过程:
for(int x = 0; x < X_MAXIMUM; ++x) { for(int y = 0; y < Y_MAXIMUM; ++y) { delete[] myArray[x][y]; } delete[] myArray[x]; } delete[] myArray;
回答
应该注意的是,出于所有目的和目的,我们只处理2D数组,因为已知第三个(也是最不重要的)维。
如果我们事先不知道数组的每个维中将有多少个条目,那么使用STL或者Boost是很好的方法,因为它们会为我们提供动态内存分配,如果数据集是保持基本上是静态的,或者如果它主要只接收新的条目而没有很多删除。
但是,如果我们事先了解数据集,例如总共将存储大约多少个项目,或者如果要稀疏地填充数组,则最好使用某种哈希/存储桶函数,并使用XYZ索引作为键。在这种情况下,假设每个维度上的条目不超过8192(13位),则可以使用40位(5字节)的密钥来获得。或者,假设始终有4 x Z个条目,则只需使用26位XY键即可。这是速度,内存使用和动态分配之间更有效的折衷之一。
回答
带有向量:
std::vector< std::vector< std::vector< int > > > array3d;
如果已经添加了元素,则可以使用array3d [x] [y] [z]访问每个元素。 (例如,通过push_back)
回答
与使用new / delete相比,使用STL管理内存有很多优点。如何表示数据的选择取决于我们计划如何使用它。一个建议是隐藏执行决策并为一维STL向量提供三维获取/设置方法的类。
如果我们确实认为需要创建自定义3d矢量类型,请先研究Boost。
// a class that does something in 3 dimensions class MySimpleClass { public: MySimpleClass(const size_t inWidth, const size_t inHeight, const size_t inDepth) : mWidth(inWidth), mHeight(inHeight), mDepth(inDepth) { mArray.resize(mWidth * mHeight * mDepth); } // inline for speed int Get(const size_t inX, const size_t inY, const size_t inZ) { return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX]; } void Set(const size_t inX, const size_t inY, const size_t inZ, const int inVal) { return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX]; } // doing something uniform with the data is easier if it's not a vector of vectors void DoSomething() { std::transform(mArray.begin(), mArray.end(), mArray.begin(), MyUnaryFunc); } private: // dimensions of data size_t mWidth; size_t mHeight; size_t mDepth; // data buffer std::vector< int > mArray; };
回答
Pieter的建议当然是好的,但是我们要记住的一件事是,在构建大型数组的情况下,它可能会非常缓慢。每次向量容量更改时,都必须复制所有数据(向量的n个向量)。
回答
以下是在每个数组的一块内存中使用C或者C ++创建3D数组的一种直接方法。无需使用BOOST(即使它很好),也无需在具有多个间接访问的行之间分配分配(这很糟糕,因为在访问数据时通常会带来很大的性能损失,并且会碎片化内存)。
唯一要了解的是,没有多维数组,只有数组(数组)的数组。最里面的索引是内存中最远的。
#include <stdio.h> #include <stdlib.h> int main(){ { // C Style Static 3D Arrays int a[10][20][30]; a[9][19][29] = 10; printf("a[9][19][29]=%d\n", a[9][19][29]); } { // C Style dynamic 3D Arrays int (*a)[20][30]; a = (int (*)[20][30])malloc(10*20*30*sizeof(int)); a[9][19][29] = 10; printf("a[9][19][29]=%d\n", a[9][19][29]); free(a); } { // C++ Style dynamic 3D Arrays int (*a)[20][30]; a = new int[10][20][30]; a[9][19][29] = 10; printf("a[9][19][29]=%d\n", a[9][19][29]); delete [] a; } }
对于实际问题,由于可能存在两个未知维度,因此我的建议存在一个问题,因为它仅允许一个未知维度。有几种方法可以解决此问题。
好消息是,现在使用变量可与C一起使用,称为变量长度数组。我们在这里查看详细信息。
int x = 100; int y = 200; int z = 30; { // C Style Static 3D Arrays int a[x][y][z]; a[99][199][29] = 10; printf("a[99][199][29]=%d\n", a[99][199][29]); } { // C Style dynamic 3D Arrays int (*a)[y][z]; a = (int (*)[y][z])malloc(x*y*z*sizeof(int)); a[99][199][29] = 10; printf("a[99][199][29]=%d\n", a[99][199][29]); free(a); }
如果使用C ++,最简单的方法可能是使用运算符重载来坚持数组语法:
{ class ThreeDArray { class InnerTwoDArray { int * data; size_t y; size_t z; public: InnerTwoDArray(int * data, size_t y, size_t z) : data(data), y(y), z(z) {} public: int * operator [](size_t y){ return data + y*z; } }; int * data; size_t x; size_t y; size_t z; public: ThreeDArray(size_t x, size_t y, size_t z) : x(x), y(y), z(z) { data = (int*)malloc(x*y*z*sizeof data); } ~ThreeDArray(){ free(data); } InnerTwoDArray operator [](size_t x){ return InnerTwoDArray(data + x*y*z, y, z); } }; ThreeDArray a(x, y, z); a[99][199][29] = 10; printf("a[99][199][29]=%d\n", a[99][199][29]); }
上面的代码在访问InnerTwoDArray时有一些间接开销(但是好的编译器可能会对其进行优化),但是只使用一个内存块来分配在堆上的数组。通常这是最有效的选择。
显然,即使上面的代码仍然简单明了,STL或者BOOST也能很好地完成,因此无需重新发明轮子。我仍然相信知道它可以轻松完成很有趣。