Трехмерные массивы целых чисел в C ++ - PullRequest
7 голосов
/ 15 сентября 2008

Я хотел бы найти безопасные способы реализации трехмерных массивов целых чисел в C ++, используя арифметическое / динамическое распределение памяти указателя или, альтернативно, используя STL методы, такие как векторы.

По сути, я хочу, чтобы размеры моего целочисленного массива выглядели так:

[ x ][ y ][ z ]

х и у находятся в диапазоне 20-6000 z известна и равна 4.

Ответы [ 7 ]

11 голосов
/ 15 сентября 2008

Взгляните на библиотеку 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++;
      }
    }
  }
}
5 голосов
/ 17 декабря 2010

Ниже приведен простой способ создания трехмерных массивов с использованием C или C ++ в одном фрагменте памяти для каждого массива. Нет необходимости использовать 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 делают это хорошо, поэтому нет необходимости заново изобретать колесо. Я все еще верю, что интересно знать, что это легко сделать.

5 голосов
/ 15 сентября 2008

Каждая пара квадратных скобок является операцией разыменования (применительно к указателю). Например, следующие пары строк кода эквивалентны:

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;
2 голосов
/ 15 сентября 2008

с векторами:

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

Каждый элемент доступен с помощью array3d [x] [y] [z], если элемент уже добавлен. (например, с помощью push_back)

1 голос
/ 15 сентября 2008

Существует множество преимуществ использования STL для управления вашей памятью по сравнению с использованием new / delete. Выбор способа представления ваших данных зависит от того, как вы планируете их использовать. Одним из предложений может быть класс, который скрывает решение о реализации и предоставляет трехмерные методы get / set для одномерного вектора STL.

Если вы действительно верите, что вам нужно создать собственный трехмерный векторный тип, сначала изучите 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;
};
1 голос
/ 15 сентября 2008

Следует отметить, что для всех намерений и целей вы имеете дело только с двумерным массивом, поскольку известно третье (и наименее значимое) измерение.

Использование STL или Boost - это неплохие подходы, если вы заранее не знаете, сколько записей у вас будет в каждом измерении массива, потому что они дадут вам динамическое распределение памяти, и я рекомендую любой из этих подходов, если ваш набор данных должен оставаться в основном статическим, или, если он в основном будет получать только новые записи, а не много удалений.

Однако, если вы заранее знаете что-то о своем наборе данных, например, сколько примерно элементов будет храниться в целом, или если массивы будут заполняться редко, вам лучше использовать какую-то функцию хеширования / сегмента, и используйте индексы XYZ в качестве ключа. В этом случае, предполагая, что не более 8192 (13 бит) записей на измерение, вы можете обойтись 40-битным (5-байтовым) ключом. Или, предполагая, что всегда есть записи 4 x Z, вы просто использовали бы 26-битный ключ XY. Это один из наиболее эффективных компромиссов между скоростью, использованием памяти и динамическим распределением.

0 голосов
/ 15 сентября 2008

Предложение Питера, конечно, хорошо, но вы должны иметь в виду, что в случае создания больших массивов оно может быть довольно медленным Каждый раз, когда емкость вектора изменяется, все данные должны копироваться (n векторов векторов).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...