【问题标题】:Allocate contiguous memory for a 3D array in C++在 C++ 中为 3D 数组分配连续内存
【发布时间】:2019-09-02 05:46:14
【问题描述】:

我能够分配连续的内存来分配 C++ 中的二维数组。我不知道如何处理 3D 数组。我已经阅读了一些帖子,但我无法提出解决方案。

#include <iostream>


using namespace std;

int main(int argc, char **argv){
    cout << "Ints have size " << sizeof(int) << endl;

int rows= 2;
int cols= 3;

int **i= new int*[rows];
int size= rows*cols;
i[0]= new int[size];
for(int j= 1; j < rows; j++) {
  i[j]= &i[0][j*cols];
}

for(int j= 0; j < rows; j++) {
  for(int k= 0; k < cols; k++) {
    cout << j << " " << k << " " << &i[j][k] << endl;
  }
}
delete[] i;
return 0;
}

【问题讨论】:

  • 让我们备份一下,你不是在分配数组,你是在为指针分配存储空间。?
  • 创建一个有足够存储空间存储整个 3D 数组的 1D 数组,然后手动执行所有索引以将 3 维映射为 1。Here is an example 在 2D 中可以扩展到 3D。
  • 重要的问题是,数组大小在编译时是否已知,或者它是否例如从文件或用户读取并且可以是任何东西?
  • @hyde 是的,它在编译时是已知的。

标签: c++ dynamic-memory-allocation


【解决方案1】:

具有P 平面的3d 数组,每个平面都有R 行和C 列,需要P*R*C 元素。您可以一次分配它们,例如:

Element *p = new Element[P*R*C];

然后要访问坐标(p, r, c) 处的元素,您可以使用公式:

int index = (p*R + r)*C + c;

使事物可读的一个简单解决方案是创建一个类

template<typename T>
struct Matrix3D {
    int P, R, C;
    std::vector<T> elements;

    Matrix3D(int P, int R, int C)
        : P(P), R(R), C(C), elements(P*R*C)
    { }

    T& operator()(int p, int r, int c) {
        return elements[(p*R + r)*C + c];
    }
};

在这个例子中,我使用std::vector 来存储元素,因为这使得所有权/复制变得更简单,并且仍然保证所有元素在内存中都是连续的。如果您想手动分配存储空间,则需要更多代码。

如果在编译时知道大小,那么您可以制作PRC 模板参数并使用std::array 成员而不是std::vector。这应该会带来一些性能改进,因为整个类最终将成为堆中的一块内存,并允许元素访问的常量乘法技巧。

【讨论】:

    【解决方案2】:

    如果您必须分配的空间必须是连续的,则必须分配一个“新”空间,否则内存将不连续。

    看起来像这样:

    int d1 = 10; // first
    int d2 = 10; // second
    int d3 = 10; // third dimension
    
    int* array3D = new int[d1 * d2 * d3];
    

    您已经为您的 3D 数组分配了足够的空间,现在必须将其映射到 3D。

    array3D[(1*d1*d2) + (2*d2) + (3)]; // access element at 1,2,3
    

    有了这个,您可以将您分配的这个 1D 数组的每个点映射到 3D 空间中的一个唯一点。

    如您所见,这很容易出错。所以你永远不应该那样做。

    永远不要使用 new/delete 来分配这样的数组:

    使用std:arraystd::vector 为您处理此问题。 使用原始的 new/delete 会导致错误,如果分配了新的任何内容而您忘记删除它,或者您忽略了某些内容,则会出现内存泄漏。

    void test(){
        int* a = new int[20];
        // do stuff with a...
        if(error)
            return; // oops this is a leak
    
        delete a; // only executed if there was no error,
    }
    

    std::array 用于如果您知道数组在编译时必须有多大,并且永远不必更改。

    另一方面,如果您在编译时不知道大小,则可以使用std::vector,它可以在您的程序运行时更改。

    std::array<int, 10> test1; // creates a fixed size array of size 10 and type int.
    std::vector<int>    test2(10); // creates an array that can change at runtime:
    test2.push_back(2);            // the vector now has size 11 and the last element is equal to 2
    

    这样你也不必delete最后的数组。

    如果您希望能够在代码中更频繁地使用它,将所有这些功能包装在一个类中会非常有帮助:

    #include <array>
    
    template<typename T, std::size_t _D1, std::size_t _D2, std::size_t _D3>
    class Array3D{
        std::array<T, _D1*_D2*_D3> elements;
    public:
        std::size_t D1(){ return _D1; }
        std::size_t D2(){ return _D1; }
        std::size_t D3(){ return _D1; }
    
        T& element(std::size_t d1, std::size_t d2, std::size_t d3){
            return elements[(d1*_D1*_D2) + (d2*_D2) + (d3)];
        }
    };
    
    int main(){ // argc/argv not required if you dont use them
        Array3D<int, 10, 10, 10> array;
        array.element(1,2,3) = 5;
    
        // loop thorug all elements
        // the methods d1,d2,d3 return the dimensions you gave them initialy
        // this way if you cange the array size you dont have to change this loop at all
        for(std::size_t i = 0; i < array.D1(); i++)
            for(std::size_t j = 0; j < array.D2(); j++)
                for(std::size_t k = 0; k < array.D3(); k++)
                    array.element(i,j,k) = 5;
    
        // no delete
    }
    

    【讨论】:

      【解决方案3】:

      数组(3D array) 的数组只不过是一个数组,它在每个索引中包含对另一个数组的引用。
      您只需分配您的第一个 2D array,然后,为该数组的每个索引,在其中分配另一个数组。

      【讨论】:

      • 3D 数组也可以是没有指针的 3 维数组。您所描述的通常称为“锯齿状数组”,因为不同的行可以具有不同的大小。
      • 没错,是的!但是用户并没有特别要求“经典数组”。他可以使用 for 循环分配索引成员,并且只需使用常量作为每个成员的大小。
      • 问题要求“连续记忆”。
      • @hyde Continous 意味着在内存中只占用一个空间范围,并不是所有的列都必须做相同的大小,不是吗?
      • @cocool97 如果你创建指针然后为它们分配内存,那么整个内存将不会是连续的——尤其是当数组大于一个虚拟页面时。
      猜你喜欢
      • 2016-12-30
      • 2020-10-23
      • 1970-01-01
      • 1970-01-01
      • 2017-01-12
      • 2021-11-12
      • 2021-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多