NunsBeachSurfer

Hi all,

I am attempting to assign values to a multidimensional array but am having difficulty.

This isn't the exact code that I'm using, but the idea is this:

for(int T=0; T<5; T++)
{
array[T][3][4] ={{4,2,6,4},{5,2,5,3},{6,3,1,5}}
}

For all T from T=0 to T=4, assign the same values shown above. That is:

Array[0][3][4] = Array[1][3][4] = ... = Array[4][3][4].

How can I go about doing this?

Thank you so much.

VernonDozier 2,218

How about making a temporary 3 x 4 array, initialize it as you have done, then in your for loop, do a memcpy of the 3 x 4 array to the address of element array[T][0][0]?

const int temp[3][4] = {{4,2,6,4},{5,2,5,3},{6,3,1,5}};
int array[5][4][3];

// Replace line 3 in your original code snippet with this...
memcpy(&array[T][0][0], &temp[0][0], sizeof(temp));

NunsBeachSurfer

That's great! It solved my problem. Thanks!

deceptikon 1,790

How about making a temporary 3 x 4 array, initialize it as you have done, then in your for loop, do a memcpy of the 3 x 4 array to the address of element array[T][0][0]?

That's fine for arrays of built-in types, but if you're working with a non-POD type then memcpy() is an exceptionally bad idea because it invokes undefined behavior.

mike_2000_17 2,669

That's a good point deceptikon, do you have a solution? I do, of course, but it gets tricky.

Of course, you can't use memcpy and expect well-defined behaviour for any non-POD type. You can manage to force an element-wise copy-construction into the destination array by the following trick:

template <typename T>
void initializeArray(T (&arr)[5][4][3]) {
for(int i = 0; i < 5; ++i) {
// create a local aggregate struct that you use to represent sub-arrays:
struct local_sub_array {
T data[4][3];
};
// create your static const sub-array:
static const local_sub_array rhs = {{{4,2,6},{4,5,2},{5,3,6},{3,1,5}}};
// use the placement-new and the copy-constructor of the local class to force
//  the use of element-wise copy-constructors if they are needed (non-POD):
new( (local_fixed_array*) &arr[i] ) local_sub_array(rhs);
};
};

The placement-new operator is very often used as a "constructor-calling memcpy replacement", but that is usually a bad idea. There is a big flaw in the above solution because the array is already constructed when you attempt to overwrite it with copy-constructed elements. You can't overwrite a constructed object, you need to destroy it first. In other words, you'll need to add this part:

template <typename T>
void initializeArray(T (&arr)[5][4][3]) {

for(int i = 0; i < 5; ++i)
for(int j = 0; j < 4; ++j)
for(int k = 0; k < 3; ++k)
arr[i][j][k].~T();       // call destructor on each element.
// NOTE: Don't attempt to reproduce this at home.
// This was done by a trained professional (me!).

for(int i = 0; i < 5; ++i) {
// create a local aggregate struct that you use to represent sub-arrays:
struct local_sub_array {
T data[4][3];
};
// create your static const sub-array:
static const local_sub_array rhs = {{{4,2,6},{4,5,2},{5,3,6},{3,1,5}}};
// use the placement-new and the copy-constructor of the local class to force
//  the use of element-wise copy-constructors if they are needed (non-POD):
new( (local_fixed_array*) &arr[i] ) local_sub_array(rhs);
};
};

But, the problem with the above is that if you are going to have to write out this triple nested loop, you might as well just do this instead:

template <typename T>
void initializeArray(T (&arr)[5][4][3]) {
static const rhs[4][3] = {{4,2,6},{4,5,2},{5,3,6},{3,1,5}};
for(int i = 0; i < 5; ++i)
for(int j = 0; j < 4; ++j)
for(int k = 0; k < 3; ++k)
arr[i][j][k] = rhs[j][k];
};

Now, the problem with the above is that it can be inefficient if your type is a POD type, because in that case, the compiler can figure out that a batch memcpy (under-the-hood) is going to be more efficient than individual assignments. So, what you really need is a batch copy-assignment. So, we can do as in the first example, and replace the batch copy-construction with a batch copy-assignment, as so:

template <typename T>
void initializeArray(T (&arr)[5][4][3]) {
for(int i = 0; i < 5; ++i) {
// create a local aggregate struct that you use to represent sub-arrays:
struct local_sub_array {
T data[4][3];
};
// create your static const sub-array:
static const local_sub_array rhs = {{{4,2,6},{4,5,2},{5,3,6},{3,1,5}}};
// use the copy-assignment of the local class to force
//  the use of element-wise copy-assignments if they are needed (non-POD):
*( (local_fixed_array*) &arr[i] ) = rhs;
};
};

Now, we have a reasonable solution which is relatively short and simple (three lines in the loop), is correct and well-behaved for non-POD types, and is as efficient as can be in any situation (POD or not).

Furthermore, if you have a C++11 capable compiler, then you have access to a nice class called std::array which also happens to be an aggregate class (like that local class I created for the purpose of casting, meaning that the standard guarantees binary compatibility with a C-style static array). In other words, you can simplify all of the above to a simple one-liner:

template <typename T>
void initializeArray(T (&arr)[5][4][3]) {
typedef std::array<std::array<T,3>,4> sub_array;
for(int i = 0; i < 5; ++i)
*( (sub_array*) &arr[i] ) = sub_array{{{4,2,6},{4,5,2},{5,3,6},{3,1,5}}};
};

Or, even this:

template <typename T>
void initializeArray(T (&arr)[5][4][3]) {
typedef std::array<std::array<T,3>,4> sub_array;
((std::array<sub_array,5>*)&arr)->fill( sub_array{{{4,2,6},{4,5,2},{5,3,6},{3,1,5}}} );
};

Simple is it not? I love C++11!

deceptikon 1,790

That's a good point deceptikon, do you have a solution?

The OP's exact needs aren't clear, so I'm not confident in offering a concrete solution, but in general for something like this I'd favor wrapping the tediousness inside an object. Something like a matrix class or nested vectors.

Is that vague enough? :D

mike_2000_17 2,669

Yes, definitely, for ease of use in the future, the OP should not rely on a static array, but create, instead, a vector/matrix/tensor class for his particular purpose. Or, at the very least, rely on a more practical static-size array representation, like std::array, if available to him.

VernonDozier 2,218

The OP's question referred to a three-dimensional array of integers, which would not be a non-POD type. The non-POD discussion is interesting, but does it apply here? memcpy will do the job just fine, and safely. Are you guys just pointing out the need to be careful and that it won't work in some OTHER scenario or do you disagree with its use in this particular case?

mike_2000_17 2,669

Well, strictly speaking, from the OP's question, there is not implication that the value-type of the array is an integer, only that it is constructible or assignable with an integer value. So, there is a slight chance that the non-POD case is relevant. Also, to me, it seemed like the relevant part of the question was on how to assign an entire sub-array (or sub-array-of-array) without having to do two nested loops to assign each individual element, so I thought it would be salient to include a general solution to that problem.

And, of course, for a POD type (like int) the memcpy solution is totally fine, given that you are aware that it does not apply to non-POD types, a fact that is relevant to point out. When applying such a method, there is a fine line between doing so because you know that it is OK in this particular case, and doing so because you think it is the general way to do it. I, like deceptikon, don't want the OP to be mislead into the latter category.