【问题标题】:Three dimensional arrays of integers in C++C ++中的整数的三维数组
【发布时间】:2010-09-08 22:00:19
【问题描述】:

我想找出在 C++ 中实现整数的三维数组的安全方法,使用指针算术/动态内存分配,或者使用STL 技术,如向量。

基本上我希望我的整数数组维度看起来像:

[ x ][ y ][ z ]

x 和 y 在 20-6000 范围内 z 是已知的,等于 4。

【问题讨论】:

  • 挖旧骨头,我知道……但是当像多维数组这样的“基本”东西随其他高级语言免费提供时,为什么人们还要使用 C++?是不是你必须使用 C++ 才能让你的双手被绑在背后?性能?
  • 嗨 MikeMurko,此时双手被绑的原因是最正确的。我会很高兴在 Java 或 C# 中做这样的事情。

标签: c++ arrays multidimensional-array


【解决方案1】:

查看 Boost multi-dimensional array 库。这是一个示例(改编自 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++;
      }
    }
  }
}

【讨论】:

    【解决方案2】:

    每对方括号都是一个解引用操作(当应用于指针时)。例如,以下几行代码是等价的:

    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;
    

    【讨论】:

    • 是的,你可以这样做,但你也可以在一个内存块中只进行一次分配。使用一个内存块通常也比使用许多小的内存块要快得多,内存间接是有代价的。当不使用完整的内部数组并且您可以完全避免分配它们时,您展示的方法对于稀疏矩阵非常有用。
    • @kriss 我完全同意,而且我个人倾向于使用单个数组(按页面分配大的东西)。我在这里设置它的原因是为了请求的语法(正如我所指出的)。访问的性能差异是微不足道的,尽管对于足够大的数组和已知的缓存行为,我当然可以创建退化的用例(对于两者),并且初始化的性能是无关紧要的(如果重要的话,你初始化它是错误的地方 - 早点做)。
    • 您也可以使用在一个内存块中要求海报的语法轻松地做到这一点。我发布了一个答案,显示(一种方式,有很多)如何使用 C++ 做到这一点,可变长度数组现在是 C99 的一个特性(太糟糕了,它没有进入 C++ 的方式)。无论如何我都会为你的答案 +1,因为它工作正常。
    【解决方案3】:

    以下是使用 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 中使用变量,它被称为变长数组。详情请看here

        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 也做得很好,因此无需重新发明轮子。我仍然相信知道它可以很容易地完成是很有趣的。

    【讨论】:

      【解决方案4】:

      使用向量:

      std::vector< std::vector< std::vector< int > > > array3d;
      

      如果元素已经被添加,每个元素都可以通过 array3d[x][y][z] 访问。 (例如通过 push_back)

      【讨论】:

        【解决方案5】:

        应该注意的是,出于所有意图和目的,您只处理一个二维数组,因为第三个(也是最不重要的)维度是已知的。

        如果您事先不知道数组的每个维度中有多少条目,则使用 STL 或 Boost 是非常好的方法,因为它们会为您提供动态内存分配,如果您的数据集将在很大程度上保持静态,或者如果它主要只接收新条目而不是很多删除。

        但是,如果您事先了解您的数据集,例如大约总共将存储多少个项目,或者如果要稀疏填充数组,则最好使用某种哈希/桶函数,并使用 XYZ 索引作为您的密钥。在这种情况下,假设每个维度不超过 8192(13 位)条目,您可以使用 40 位(5 字节)密钥。或者,假设总是有 4 x Z 条目,您只需使用 26 位 XY 密钥。这是速度、内存使用和动态分配之间更有效的权衡之一。

        【讨论】:

          【解决方案6】:

          与使用 new/delete 相比,使用 STL 管理内存有很多优势。如何表示数据的选择取决于您计划如何使用它。一个建议是隐藏实现决策并为一维 STL 向量提供三维 get/set 方法的类。

          如果您确实认为需要创建自定义 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;
          };
          

          【讨论】:

            【解决方案7】:

            Pieter 的建议当然很好,但您必须记住的一件事是,在构建大型阵列的情况下,它可能会很慢。每次向量容量发生变化时,都必须复制所有数据(“n”个向量向量)。

            【讨论】:

            • 您可以轻松地预先分配
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-12-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-21
            • 2010-09-09
            • 1970-01-01
            相关资源
            最近更新 更多