Reputation: 2682
I am using a library that takes a pointer to a double array as an argument, called as follows:
const int N=10;
const int J=20;
double my_array[N*J];
libFunc(my_array, N, J);
I would prefer to work with multidimensional arrays in my code, and I have discovered that I can call libFunc
by dereferencing my multidimensional double array as follows
double my_array2[N][J];
libFunc(&my_array2[0][0], N, J);
However, I am worried that this code might not be portable, that it may not continue to work as N and M get large, and that there may be other hidden problems.
Why is this bad, what should I look out for here? What is the proper way to use multidimensional arrays and pass them to libFunc
as if they were ordinary double arrays?
Edit: Read the comments below the selected answer for a discussion of the issue at hand. It seems that if I declare a static array, as is done above, then this code should work on most compilers. However if the array is dynamically allocated there may be an issue.
Upvotes: 2
Views: 354
Reputation: 2702
C++ uses row-major ordering so your multidimensional array is in fact a continuous 1-dimensional region in memory.
Even if declared for example 2-dimensional, it's accessed via index = x + y * height
, so there should be no portability concerns...
The C++ documentation tells:
Multidimensional arrays are just an abstraction for programmers, since we can obtain the same results with a simple array just by putting a factor between its indices
(Here's also an explaination for visual c++)
Upvotes: 1
Reputation: 83414
You are basically screwed: The function expects a double *
, so you should give it a double *
.
The easiest and safer way to do that would be to use a wrapper. Something like:
template<size_t M, size_t N>
class Double2DArray
{
std::vector<double> m_container ; // It could be a double[M * N]
// But it could explode your stack
// So let's use the heap
public :
// Etc.
Double2DArray()
: m_container(M * N)
{
// I assume the default you want is a vector already
// filled with 0.0
}
size_t getSizeM() { return M ; }
size_t getSizeN() { return N ; }
double & operator() (size_t p_m, size_t p_n)
{
return m_container[p_m * N + p_n] ;
}
double * data()
{
return &(m_container[0]) ;
}
// etc.
} ;
Of course, this code is not complete: At the very least, you should add the const versions of the accessors, probably handle copy-construction and assignment, etc.. I don't know your exact needs, so, your mileage may vary, but the core idea is there...
You could use this wrapper as follow:
void foo()
{
Double2DArray<42, 13> my2DArray ;
// etc.
my2DArray(3, 5) = 3.1415 ; // set some value
double d = my2DArray(13, 2) ; // get some value
// etc.
libFunc(my2DArray.data(), my2DArray.getSizeM(), my2DArray.getSizeN());
}
I would even overload libFunc to be safer:
template<size_t M, size_t N>
inline void libFunc(Double2DArray<M, N> & p_my2DArray)
{
libFunc(p_my2DArray.data(), M, N);
}
This way I could be able to call it without needed to give it again and again the size of the array (it's so easy to mix M and N):
void foo()
{
Double2DArray<42, 13> my2DArray ;
// etc.
libFunc(my2DArray);
}
This is how I would use multidimensional arrays and feed it to a C-like API expected a contiguous array of doubles.
P.S.: If M and N are not know at compile time, you only need to remove the template, and make the M and N parameters of the constructor, and everything works (almost) the same.
Upvotes: 1
Reputation: 477710
There is no simple way short of making a copy. Accessing an array outside its bounds is undefined behaviour, and you won't get around this.
Now, it is possible in many situations that your code works, simply because the memory for T[M * N]
and for T[M][N]
is laid out in the same way. As long as the caller and the callee aren't visible to the compiler at the same time, I would hazard a guess that this should work.
But imagine this scenario:
T a[M][N];
for (size_t i = 0; i != M * N; ++i)
{
a[0][i] = 0;
}
Here the compiler may reason that a[0][N]
is out of bounds, and thus there is undefined behaviour, and the compiler may legally omit the entire loop, or make the application crash or wipe your hard disk.
So... take your pick. Undefined behaviour is around somewhere, but you might get lucky.
Upvotes: 2