What is the safe way to fill multidimensional array using std::fill?
Here is what I am using:
class something
{
char flags[26][80];
} a;
std::fill(&a.flags[0][0], &a.flags[0][0] + 26 * 80, 0);
(Update: I should have made it clear earlier that I am using this inside a class.)
Solution 1:
The simple way to initialize to 0
the array is in the definition:
char flags[26][80] = {};
If you want to use std::fill
, or you want to reset the array, I find this a little better:
char flags[26][80];
std::fill( &flags[0][0], &flags[0][0] + sizeof(flags) /* / sizeof(flags[0][0]) */, 0 );
The fill
expressed in terms of the array size will allow you to change the dimensions and keep the fill
untouched. The sizeof(flags[0][0])
is 1
in your case (sizeof(char)==1
), but you might want to leave it there in case you want to change the type at any point.
In this particular case (array of flags --integral type) I could even consider using memset
even if it is the least safe alternative (this will break if the array type is changed to a non-pod type):
memset( &flags[0][0], 0, sizeof(flags) );
Note that in all three cases, the array sizes are typed only once, and the compiler deduces the rest. That is a little safer as it leaves less room for programmer errors (change the size in one place, forget it in the others).
EDIT: You have updated the code, and as it is it won't compile as the array is private and you are trying to initialize it externally. Depending on whether your class is actually an aggregate (and want to keep it as such) or whether you want to add a constructor to the class you can use different approaches.
const std::size_t rows = 26;
const std::size_t cols = 80;
struct Aggregate {
char array[rows][cols];
};
class Constructor {
public:
Constructor() {
std::fill( &array[0][0], &array[rows][0], 0 ); // [1]
// memset( array, 0, sizeof(array) );
}
private:
char array[rows][cols];
};
int main() {
Aggregate a = {};
Constructor b;
}
Even if the array
is meant to be public, using a constructor might be a better approach as it will guarantee that the array
is properly initialized in all instances of the class, while the external initialization depends on user code not forgetting to set the initial values.
[1] As @Oli Charlesworth mentioned in a comment, using constants is a different solution to the problem of having to state (and keep in synch) the sizes in more than one place. I have used that approach here with a yet different combination: a pointer to the first byte outside of the bidimensional array can be obtained by requesting the address of the first column one row beyond the bidimensional array. I have used this approach just to show that it can be done, but it is not any better than others like &array[0][0]+(rows*cols)
Solution 2:
What is the safe way to fill multidimensional array using
std::fill
?
The easy default initialization would be using braced inilization.
char flags[26][80]{};
The above will initialize all the elements in the flags
to default char.
2-D Array filling using std::fill
or std::fill_n
However, in order to provide different value to initialize the above is not enough. The options are std::fill
and std::fill_n
. (Assuming that the array flags
is public
in your class)
std::fill(
&a.flags[0][0],
&a.flags[0][0] + sizeof(a.flags) / sizeof(a.flags[0][0]),
'0');
// or using `std::fill_n`
// std::fill_n(&a.flags[0][0], sizeof(a.flags) / sizeof(a.flags[0][0]), '1');
To generalize this for any 2d-array of any type with any initializing value, I would suggest a templated function as follows. This will also avoid the sizeof
calculation of the total elements in the array.
#include <algorithm> // std::fill_n, std::fill
#include <cstddef> // std::size_t
template<typename Type, std::size_t M, std::size_t N>
constexpr void fill_2D_array(Type(&arr2D)[M][N], const Type val = Type{}) noexcept
{
std::fill_n(&arr2D[0][0], M * N, val);
// or using std::fill
// std::fill(&arr2D[0][0], &arr2D[0][0] + (M * N ), val);
}
Now you can initialize your flags
like
fill_2D_array(a.flags, '0'); // flags should be `public` in your class!
(See Live Online)
3-D Array filling using std::fill
or std::fill_n
Adding one more non-template size parameter to the above template function, this can be brought to 3d-arrays as well
#include <algorithm> // std::fill_n
#include <cstddef> // std::size_t
template<typename Type, std::size_t M, std::size_t N, std::size_t O>
constexpr void fill_3D_array(Type(&arr3D)[M][N][O], const Type val = Type{}) noexcept
{
std::fill_n(&arr3D[0][0][0], M * N * O, val);
}
(See Live Online)
Solution 3:
it is safe, a two-dimensional array is an array of arrays. Since an array occupied contiguous storage, so the whole multidimensional thing will too. So yeah, it's OK, safe and portable. Assuming you are NOT asking about style, which is covered by other answers (since you're using flags, I strongly recommend std::vector<std::bitset<80> > myFlags(26)
)
Solution 4:
char flags[26][80];
std::fill((char*)flags, (char*)flags + sizeof(flags)/sizeof(char), 0);