Link to home
Start Free TrialLog in
Avatar of joeslow
joeslow

asked on

Dynamic multidimensional array of doubles

Hi,

I need to declare a large three dimensional array of doubles.  If I do this:
double arr[200][200][400];
I get a runtime stack overflow error.
So I need to create it dynamically like
double (*arr)[200][400] = new double[200][200][400];

But now I don't want the sizes to be predefined.  How can I declare a 3D array of doubles at runtime, use it (assign values to it), and clean up when finished?

Thank you,
Joe
ASKER CERTIFIED SOLUTION
Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of edwardt
edwardt

The ugly truth is that neither C or C++ have a varible array dimension feature, said feature has been in other languages like FORTRAN for 30+ years now.

You can cobble up something with macros, but it won't be pretty.

Alternatives include:  doing the variable array stuff in another language (FORTRAN, Pascal, Java), or use a large fixed size array.   With memory going for 10 cents a megabyte, this is often the fastest and cheapest option.

You can use std::vector<double>.

e.g.:
--------8<--------
#include <iostream>
#include <vector>
#include <iomanip>

typedef std::vector<double> DoubleV;
typedef std::vector<DoubleV> DoubleVV; // 2D "array" of doubles
typedef std::vector<DoubleVV> DoubleVVV; // "Array" of 2D "array" of doubles

int main()
{
      DoubleVVV dVVV(10);
      double value = 0.0;
      for (int i = 0;i < 10;i++) {
            DoubleVV& dVV = dVVV[i];
            dVV.resize(100);
            for (int x = 0;x < 100;x++) {
                  DoubleV& dV = dVV[x];
                  dV.resize(100);
                  for (int y = 0;y < 100;y++)
                        dV[y] = value += 1.00001;
            }
      }

      {
            int i,x,y;
            std::cout << "Choose an array 0..9: ";std::cin >> i;
            std::cout << "Choose an x-coord 0..99: ";std::cin >> x;
            std::cout << "Choose a y-coord 0..99: ";std::cin >> y;
            std::cout << std::fixed << std::setprecision(5);
            if (i >= 0 && i < 10 && x >= 0 && x < 100 && y >= 0 && y < 100)
                  std::cout << "Value is: " << dVVV[i][x][y] << '\n';
      }
}
--------8<--------
> Alternatives include:  doing the variable array stuff in another language (FORTRAN, Pascal, Java),
I think that is NEVER a good alternative. Object Oriented languages was designed to permit you design YOU OWN variables types. Never a languages will have all variable types you want because every user has a different requirement.
So, when you don't have the type you want, design it.
Some examples of not pre-made types:
- complex numbers
- fractional numbers
- huge integers
- huge floating point numbers
- 3 dimensional dinamic arrays
.....etcetera

C, and so C++, certainly do have support for variable arrays. The syntax doesn't allow you to create them directly, but they can be created with a little work. They can then be used with the built in syntax. Suppose your three dimensions are stored in variables d1, d2 and d3. You would then dynamically construct the array as follows:

double **mx;
mx=new double **[d1];
for(int i=0; i<d1; ++i)
{   mx[i]=new double *[d2];
    for(int j=0; j<d2; ++j)mx[i][j]=new double[d3];
}

The items in the matrix can now be accessed with the standard array syntax, e.g.:

mx[1][5][2]=0.00;

freeing the dynamic memory requires the reverse process.
Avatar of joeslow

ASKER

Hi,

Thanks to all who answered.  When I first asked this question I didn't realize what a problem it would be.  Anyway, I liked Jaime's approach because in the end, I need to pass the array to a MATLab dll so the vector stuff won't work in my case - I guess I should have mentioned that.  Anyway, now I can use the data class member as the array I pass.

Thanks again,
Joe
> I need to pass the array to a MATLab dll so the vector stuff won't work in my case

You can pass arrays of doubles with the vector approach

// External function handles an array of doubles
extern "C" void f(double values[],size_t count);

// Call it with this.
f(&dVVV[i][x][0],dVVV[i][x].size());
!>C, and so C++, certainly do have support for variable arrays.
!>The items in the matrix can now be accessed with the standard array syntax, e.g.:
!>mx[1][5][2]=0.00;


Pls explain how the compiler knows at run-time the dimensions of array mx, in order to correctly execute the above statement.

You also didnt mention this requires at least 50% extra memory, and double the memory reference time for the indirection, not to mention defeating any compiler's attempt at optimizing or allocating floating-point registers.




> not to mention defeating any compiler's attempt at optimizing or allocating floating-point registers

Could you expand on this grg99?
Avatar of joeslow

ASKER

I found this explanation pretty interesting but I don't know if it's what you guys are talking about (scroll to bottom of page):

http://www.cs.wfu.edu/~torgerse/fftw_1.2/fftw_6.html#SEC31

Joe
Interesting article!
As author says, you have to choose between easy to use/mantain and high performance. Not always high performace is better to easy to use and maintain.
>> not to mention defeating any compiler's attempt at optimizing or allocating floating-point registers

>Could you expand on this grg99?

Just that the compiler has no idea that what's pointed to by mx[1][1][1] is next to mx[1][1][2], so any loop that processes the whole arrray sequentially cant benefit from all the strength reductions of changing multiplies to adds.   Likewise the compiler can't do any data-flow analysis on the F.P. data, so it has to assume the worst case, and it can't keep mx[a][b][c] in a F.P. register, because it could be aliased to mx[d][e][f].   And on a register-poor CPU like the x86, doing everything by indirection ties up one or more of the precious address-capable registers (really bad in 16-bit or segmented 32-bit modes, not quite so bad in 32-bit flat mode).


Alternatives include:  doing the variable array stuff in another language (FORTRAN, Pascal, Java),
I think that is NEVER a good alternative. Object Oriented languages was designed to permit you design YOU OWN variables types.

... except a good 80% of the folks that would like to use variable-dim arrays are just average math and physics geeks, few of which are also capable enough to design a bulletproof multi-dim-array object.  

... and C++ is particularly weak in giving one any traction in this particular area, since there's no syntax for overloading multiple subscripts.


You can do something kludgy like this, suggested by the C++ FAQ:

class Matrix {
 public:
   Matrix(unsigned rows, unsigned cols);
   double& operator() (unsigned row, unsigned col);
   double  operator() (unsigned row, unsigned col) const;
   ...
  ~Matrix();                              // Destructor
   Matrix(const Matrix& m);               // Copy constructor
   Matrix& operator= (const Matrix& m);   // Assignment operator
   ...
 private:
   unsigned rows_, cols_;
   double* data_;
 };
 
 inline
 Matrix::Matrix(unsigned rows, unsigned cols)
   : rows_ (rows)
   , cols_ (cols)
   //data_ <--initialized below (after the 'if/throw' statement)
 {
   if (rows == 0 || cols == 0)
     throw BadIndex("Matrix constructor has 0 size");
   data_ = new double[rows * cols];
 }
 
 inline
 Matrix::~Matrix()
 {
   delete[] data_;
 }
 
 inline
 double& Matrix::operator() (unsigned row, unsigned col)
 {
   if (row >= rows_ || col >= cols_)
     throw BadIndex("Matrix subscript out of bounds");
   return data_[cols_*row + col];
 }
 
 inline
 double Matrix::operator() (unsigned row, unsigned col) const
 {
   if (row >= rows_ || col >= cols_)
     throw BadIndex("const Matrix subscript out of bounds");
   return data_[cols_*row + col];
 }

-------------------

You still need seperate classes for each number and type of dimension, but it's slightly better than nothing....

 
>... except a good 80% of the folks that would like to use variable-dim arrays are just average math and physics geeks,
> few of which are also capable enough to design a bulletproof multi-dim-array object.  
That's why they are asking us

>... and C++ is particularly weak in giving one any traction in this particular area, since there's no syntax for overloading
> multiple subscripts
Overloading is one of many alternatives, created to facilitate reading of code. Personally I almost never use overloading. Using a object's functions has exactly the same effect. Overloaded operator is a function whos name is a symbol.


Thanks for the clarification. I'm with you now. However, I wonder how realistic it is to question the efficiency of fetches like a_slow_array[i][j][k], when code that uses [][][] is unlikely (or at least oughtn't to be) in a loop anyhow. In a real world situation, I'd expect and expression like a_slow_array[i][j][k] to appear in something that was I/O bound and any loop processing to use iterators/pointers. I'd need to see what the profiler had to say, before.

> ... and C++ is particularly weak in giving one any traction in this particular area, since there's no syntax for overloading multiple subscripts.

You can, however, use adapter classes.

e.g. http://www.boost.org/libs/multi_array/doc/user.html

Wow!    I didnt know you could do THAT !

I do tremble a bit at the idea of templates generating templates....

In which case, you'll either love or hate Modern C++ Design: Applied Generic and Design Patterns by Andrei Alexandrescu - see http://www.moderncppdesign.com/ 

Alexandrescu is the guy who came up with Loki, which I must confess that I'm struggling with (being a bear of little brain) but that I am strangely attracted to. It takes templates one step beyond.