salute..
I am learning dynamic allocations for multidimensional arrays in a book and I found some ways for that, And now haven't problem in it.
But the author of the book shows us a way, but it doesn't work correctly. It is this:
pbeans = new double [3][4]; // Allocate memory for a 3x4 array
And this is the error:
error C2440: '=' : cannot convert from 'int (*)[4]' to 'int *'
how should i define pbeans ( if this type of coding is legal)?
and what is the problem exactly?
Regards.
This is covered in my FAQ on arrays:
double (*pbeans)[4];
pbeans = new double[3][4];
// ...
delete[] pbeans;
For the "C declarator impaired", you could make that more readable with a typedef:
typedef double row[4];
row *pbeans;
pbeans = new row[3];
// ...
delete[] pbeans;
But in C++, we prefer RAII containers over raw pointers:
#include <vector>
#include <array>
std::vector<std::array<double, 4> > beans(3);
Note the complete absence of delete[] which makes this solution exception-safe.
You need to allocate each dimension of the array separately:
double **pbeans = new double*[3];
for (int i = 0; i < 3; ++i) {
pbeans[i] = new double[4];
}
Here is a way to do it that allocates the memory contiguously on the heap:
typedef double MyGrid[3][4];
int main(int argc, char* argv[])
{
MyGrid& x = *(reinterpret_cast<Grid*>(new double[12]));
...
x[1][2] = 0.3333;
...
delete[] &x;
return 0;
}
Which you could easily turn into a more generic solution:
template<typename T, int x, int y>
struct Array2D
{
typedef T CArrayType[x][y];
typedef CArrayType& RefType;
static CArrayType& New()
{
return *(reinterpret_cast<CArrayType*>(new T[x * y]));
}
static void Delete(RefType x)
{
delete[] &x;
}
};
typedef Array2D<double, 3, 4> MyGrid;// define your 2d array with 3 rows / 4 columns.
int main(int argc, char* argv[])
{
MyGrid::RefType j = MyGrid::New();
...
j[1][2] = 0.3333;
...
MyGrid::Delete(j);
return 0;
}
The idea is to just generate the elements in 1D (x*y), and cast it to a 2D array. But since array types are value types, you need to deal in pointers-to-arrays. Using a reference makes it almost transparent.
Boost probably has something like this but I don't know boost well enough to say...
Related
I have to create an array of array named m2DArray. It has 2 rows and 5 cols i;e a size of [2][5].
The array can be float** or double** which is known to me only at runtime.
Since I do not know the type at compile time, I initialize it in my header files as
void **m2DArray;
Then I create a template function:
template <typename SampleType>
void MyClass::initiliaze2DArray(SampleType** m2DArrayTyped)
{
m2DArray = new SampleType* [2]; //2 rows
int32 sizeOfOneCol = 5 * sizeof(SampleType); // 5 cols
for (int32 row = 0; row < 2; row++)
{
m2DArray[row] = new SampleType [sizeOfOneColumn];
memset(m2DArray[row], 0, sizeOfOneCol);
}
}
Then at runtime I decide between float** or double** based on some logic in the class and accordingly try to initalize the 2d array.
if (somelogictellsfloat){
float **mArrayFloat;
initiliaze2DArray(mArrayFloat);
}
else {
double **mArrayDouble;
initiliaze2DArray(mArrayDouble);
}
However, when trying to initialize this 2d array, I am not able to convert the void** to either float** or double** in my template function.
I get the following error:
error: invalid conversion from ‘double**’ to ‘void**’ in line m2DArray = new SampleType* [2];
My Question:
How do I cast **m2DArray from void ** to float ** or double ** ? Or is there a better way to create this 2d array at runtime.
You have some misunderstandings here. Most important: void** is NOT a generic pointer. The only generic pointer is void*. So, your type void** can hold the address of a generic void*. This you need to understand.
So, regardless, how many dimensions your array would have, you could assign it to a void*. And later, it is your responsibility to cast it to your needed type. See:
// Create a 5 dimensional array
char***** dimension5 = static_cast<char*****>( malloc(200));
// Assign it to a generic pointer
void* generic = dimension5;
// Get the address of the generic pointer
void** addressOfGeneric = &generic;
// Now dereference the address of the generic pointer to get back the generic pointer
// and cast it to our original type
char***** dimension5Later = static_cast<char*****>(*addressOfGeneric);
// Do something with the array
dimension5Later[0][0][0][0][0] = 'H';
But, of course we would never do that. In C++ we should never use raw pointers for owned memory. We should try to avoid raw pointers at all and work with smart pointers. We must not use malloc and we should not even use new and instead some make_unique-function.
I wonder, if you need that at all. Because you should use std::vector instead.
Please see the following:
// The dimension of our 2d vector
constexpr size_t NumberOfRows = 2U;
constexpr size_t NumberOfColumns = 5U;
bool somelogictellsfloat{ true };
// Depending on what to create
if (somelogictellsfloat) {
// Create and initialize a 2d vector for floats
std::vector<std::vector<float>> mArrayFloat(NumberOfRows, std::vector<float>(NumberOfColumns, 0.0));
}
else {
// Create and initialize a 2d vector for doubles
std::vector<std::vector<double>> mArrayDouble(NumberOfRows, std::vector<double>(NumberOfColumns, 0.0));
}
But what you really want, is to use the abstract factory pattern. If you do not know, the please read about this.
By the way. I made for your original code a minimum reproducable example. This you should always do in questions on SO.
I fixed some bugs and made it compilable. But please do not use
#include <cstring>
#include < cstdlib >
#include <vector>
struct MyClass {
void** m2DArray; // Address of a generic pointer
void* md;
float** mArrayFloat;
double** mArrayDouble;
using int32 = int;
template <typename SampleType>
void initiliaze2DArray(SampleType** m2DArrayTyped)
{
m2DArrayTyped = new SampleType * [2]; //2 rows
int32 sizeOfOneCol = 5 * sizeof(SampleType); // 5 cols
for (int32 row = 0; row < 2; row++)
{
m2DArrayTyped[row] = new SampleType[5];
memset(m2DArrayTyped[row], 0, sizeOfOneCol);
}
md = m2DArrayTyped; // md is now a generic pointer
m2DArray = &md; // and m2DArray is the address of that generic pointer
}
// Test function
void test(bool doFloatAndNotDouble) {
if (doFloatAndNotDouble) {
initiliaze2DArray(mArrayFloat);
}
else {
initiliaze2DArray(mArrayDouble);
}
}
};
// Driver code
int main() {
MyClass mc{};
mc.test(true);
mc.test(false);
return 0;
}
How to create a 2d array using dynamic memory allocation in c++?
Maze(int c=10){
const int m=c;
a=new int[m][m];
}
void main(){
Maze(12);
}
std::vector is the typical way to have a dynamically allocated array in C++. You can have a vector of vectors to make it two-dimensional. Here's an example:
std::vector<std::vector<int>> a(m,std::vector<int>(m));
If you want it inside a class:
struct Maze {
std::vector<std::vector<int>> a;
Maze(int m) : a(m,std::vector<int>(m)) { }
};
Easily - using multiplication. Also I suggest using reference to array because in this way you specify the type more explicitly then using a pointer to it's first element. I'm actually amazed why this isn't the type most programmers use. Perhaps because they're lazy and the type is complex ;).
void Maze(int c=10) {
const int m=c;
int (&a)[0][0] = *(int (*)[0][0])new int[/*numbers of rows*/ m * sizeof(int) * m /* number of colums on each row*/];
}
Here 'a' is an reference to the newly created array. As types aren't dynamic in 'C++' language we assume that it has zero elements on each of it's dimensions. But of-course we can access more then 0.
Now if you have a function with parameter of type 2 dim array it will look like this:
void func(int (&_2dimarray)[0][0]) ;
Or if you want to return it from your 'Maze' you could write:
int (&Maze(int c=10))[0][0] {
const int m=c;
int (&a)[0][0] = *(int (*)[0][0])new int[/*numbers of rows*/ m * sizeof(int) * m /* number of colums on each row*/];
return a;
}
Life example.
But of-course the easiest way is using 'std::vector' which however can have performance cost on some compilers while the built-in array will more surely run fast everywhere.
EDIT: The explanation is simple - the 'new []' can be thought as a function like:
template<class T>
T *operator new T[] (std::size_t);
Your instance of it:
a=new int[m][m];
Can also look like this (illustrative)
a=operator new int[m][](m);
Which fulfills 'T' with 'int[m]'.
This is illegal because 'int[m]' is not valid type. 'C++' supports only static types and this is not such because the length of the array can't be determined during compile-time as 'm' is not a constant. The last 'm' is a function parameter to 'operator new[]'.
Yep I also think this construct isn't the most elegant yet but this is the life.
There are two approaches. If the size of the internal one-dimensional subarray is a constant value known at compile time then you can write
const size_t N = 10;
int ( * )[N] Maze( size_t n = N )
{
return new int[n][N];
}
int main()
{
int ( *a )[N] = Maze( 12 );
//...
delete [] a;
}
If it is not a constant then you need to allocate a one-dimensional array of pointers to one-dimensional arrays. For example
const size_t N = 10;
int ** Maze( size_t n = N )
{
int **p = new int *[n];
for ( size_t i = 0; i < n; i++ ) p[i] = new int[n];
return p;
}
int main()
{
int **a = Maze( 12 );
//...
for ( size_t i = 0; i < 12; i++ ) delete [] a[i];
delete [] a;
}
Also you could use smart pointers as for example std::unique_ptr.
The other approach is to use standard container std::vector<std::vector<int>>
So if I have a class with a 2D array that I want to initialize with two parameters passed into the constructor, how would I do that, I keep running into errors because it won't let me update the two-d array at all in the constructor.
-- Update from the comments:
In my header file I tried both
int array[][]
and
int **array
and then in the .cpp file in the constructor I'm trying to do
array = new int[arg1][arg2]
Neither declaration of the array in the header file worked.
in the constructor I'm trying to do array = new array[arg1][arg2]
You need to specify the array type, like
array = new int[arg1][arg2];
Note that this works in C++11 only - when using older standards, the second array size needs to be const (which is probably not what you want).
There are also some additional articles discussing the same issue:
Multi-Dimensional Arrays
How to "new" a two-dimension array in C++?
Ideally, since you are using C++ anyway, you should use std::vector as proposed in another answer.
Vectors use a lot of overhead though, don't they? I'm trying to keep my memory use light. –
Start with std::vector. Once your application is running properly from a functional perspective, if you are still concerned about memory usage and/or performance, do benchmarking. If you properly encapsulate your 2D array in a class, you can always change the actual implementation of the array with no impact on the code which uses it.
Technically, if you want to make sure that you have one flat memory area which contains your array, you could use a 1-dimensional array to simulate a 2-dimensional array, like in the following code (just to get you the idea, certainly needs some improvement, especially copy construction and assignment operators are missing):
class Array2D {
private:
int *array;
int size1;
public:
Array2D(int arg1, int arg2) {
size1 = arg1;
array = new int[arg1 * arg2];
}
~Array2D() {
delete[] array;
}
int& at(int i1, int i2) {
return array[i1 * size1 + i2];
}
};
int main() {
Array2D array(10, 10);
array.at(2, 2) = 42;
std::cerr << array.at(2, 2);
return 0;
}
Simplest solution would be:
std::vector<std::vector<VALUE>> arr2(X, std::vector<VALUE>(Y));
Here is an 2d array example with bounds check and custom type, based upon the example from Andreas Fester.
#include <stdexcept>
template <typename T>
class Array2D {
private:
T *array;
unsigned int sizeX;
unsigned int sizeY;
public:
Array2D(unsigned int X, unsigned int Y) {
sizeX = X;
sizeY = Y;
array = new T[X * Y];
}
~Array2D() {
delete[] array;
}
T& at(unsigned int X, unsigned int Y) {
if((X > sizeX) || (Y > sizeY))
throw std::out_of_range("Bla bla");
return array[X * sizeX + Y];
}
};
int main() {
double MyValue;
Array2D<double> *MyArray = new Array2D<double>(10, 100);
MyArray->at(1,1) = 10.1;
MyValue = MyArray->at(1,1);
printf("Array value = %3.3f\n", MyValue);
return 0;
}
I am pretty new to C++ with Boost.
I want an object of class "world" to have an array named "chunk" of type "octreenode". Previously I had an ordinary one-dimensional array, and this worked fine. Now I'm trying to move to using a 3D array with Boost's multi_array functionality, and I'm really not sure what I'm doing wrong.
Simplified code:
class world {
public:
typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
typedef planetchunkarray::index index;
planetchunkarray *chunk;
world(double x,double y,double z,
int widtheast, int widthnorth, int height) :
originx(x), originy(y), originz(z),
chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height) {
chunk = new planetchunkarray(boost::extents[chunksnorth][chunkseast][chunksup]);
planetchunkarray::extent_gen extents;
for (int cz = 0; cz < chunksnorth; ++cz) {
for (int cx = 0; cx < chunkseast; ++cx) {
for (int cy = 0; cy < chunksup; ++cy) {
(*chunk)[cz][cx][cy] = new octreenode(1,72);
}
}
}
}
};
After which if I attempt to make the assignment
root->planet[0]->chunk[0][0][0]->material = 4;
I get the error:
error: base operand of '->' has non-pointer type 'boost::detail::multi_array::sub_array<octreenode, 1u>'|
"octreenode" has the relevant constructor, and this line worked in identical syntax when it was just:
root->planet[0]->chunk[0]->material = 4;
(with a one-dimensional array). Similarly, while it compiled fine with a one-dimensional array, trying to pass the chunk to functions that expect a pointer to an "octreenode" object, such as:
compactoctree(root->planet[p]->chunk[cz][cx][cy], 0, 14);
generates the error
error: cannot convert 'boost::detail::multi_array::sub_array<octreenode, 1u>' to 'octreenode*' for argument '1' to 'short int compactoctree(octreenode*, int, int)'|
Would be very grateful for any suggestions, I'm sure I'm missing something obvious.
Your array is of value type (octreenode), not pointer type (octreenode*)
Therefore you are not supposed to try to assign a pointer to a dynamically allocated octreenode (new is for heap allocation, by default).
Instead, just assign a value:
(*chunk)[cz][cx][cy] = octreenode(1,72);
In fact, there's no reason to use new on the multi array in the first place either:
UPDATE
In the comments it has been raised that more things could be optimized and that you consider that useful additions to the answer about the compilation error.
So here goes: if you indeed want to initialize all array elements with the exact same value,
You can make the loops way more efficient by forgetting about the array shapes for a moment:
std::fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
If you know octreenode is a POD type, you could write
std::uninitialzed_fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
but a smart library implementation would end up calling fill_n anyways (because there's no gain). You can use uninitialized_fill_n if octreenode is not a POD type, but it is trivially destructible.
In fact, there's no reason to use new on the multi array in the first place either. You can just use the constructor initialization list to construct the multi_array member
Live On Coliru
#include <boost/multi_array.hpp>
#include <type_traits>
struct octreenode { int a; int b; };
class world {
public:
world(double x, double y, double z, int widtheast, int widthnorth, int height)
:
originx(x), originy(y), originz(z),
chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height),
chunk(boost::extents[chunksnorth][chunkseast][chunksup])
{
octreenode v = { 1, 72 };
std::fill_n(chunk.data(), chunk.num_elements(), v);
}
private:
double originx, originy, originz;
int chunkseast, chunksnorth, chunksup;
typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
typedef planetchunkarray::index index;
planetchunkarray chunk;
};
int main() {
world w(1,2,3,4,5,6);
}
I am writing some C++ code in Linux where I have declared a few 2D arrays like so:
double x[5000][500], y[5000][500], z[5000][500];
During compilation there is no error. When I execute it says "segmentation fault".
Wen I reduce the size of the array from 5000 to 50, the program runs fine. How can I protect myself against this problem?
If your program looks like this ...
int main(int, char **) {
double x[5000][500],y[5000][500],z[5000][500];
// ...
return 0;
}
... then you are overflowing the stack. The fastest way to fix this is to add the word static.
int main(int, char **) {
static double x[5000][500],y[5000][500],z[5000][500];
// ...
return 0;
}
The second fastest way to fix this is to move the declaration out of the function:
double x[5000][500],y[5000][500],z[5000][500];
int main(int, char **) {
// ...
return 0;
}
The third fastest way to fix this is to allocate the memory on the heap:
int main(int, char **) {
double **x = new double*[5000];
double **y = new double*[5000];
double **z = new double*[5000];
for (size_t i = 0; i < 5000; i++) {
x[i] = new double[500];
y[i] = new double[500];
z[i] = new double[500];
}
// ...
for (size_t i = 5000; i > 0; ) {
delete[] z[--i];
delete[] y[i];
delete[] x[i];
}
delete[] z;
delete[] y;
delete[] x;
return 0;
}
The fourth fastest way is to allocate them on the heap using std::vector. It is fewer lines in your file but more lines in the compilation unit, and you must either think of a meaningful name for your derived vector types or tuck them into an anonymous namespace so they won't pollute the global namespace:
#include <vector>
using std::vector
namespace {
struct Y : public vector<double> { Y() : vector<double>(500) {} };
struct XY : public vector<Y> { XY() : vector<Y>(5000) {} } ;
}
int main(int, char **) {
XY x, y, z;
// ...
return 0;
}
The fifth fastest way is to allocate them on the heap, but use templates so the dimensions are not so remote from the objects:
include <vector>
using namespace std;
namespace {
template <size_t N>
struct Y : public vector<double> { Y() : vector<double>(N) {} };
template <size_t N1, size_t N2>
struct XY : public vector< Y<N2> > { XY() : vector< Y<N2> > (N1) {} } ;
}
int main(int, char **) {
XY<5000,500> x, y, z;
XY<500,50> mini_x, mini_y, mini_z;
// ...
return 0;
}
The most performant way is to allocate the two-dimensional arrays as one-dimensional arrays, and then use index arithmetic.
All the above assumes that you have some reason, a good one or a poor one, for wanting to craft your own multidimensional array mechanism. If you have no reason, and expect to use multidimensional arrays again, strongly consider installing a library:
A plays-nicely-with-STL way is to
use the Boost Multidimensional
Array.
A speed way is to use Blitz++.
These arrays are on the stack. Stacks are quite limited in size. You probably run into a ... stack overflow :)
If you want to avoid this, you need to put them on the free store:
double* x =new double[5000*5000];
But you better start the good habit of using the standard containers, which wrap all this for you:
std::vector< std::vector<int> > x( std::vector<int>(500), 5000 );
Plus: even if the stack fits the arrays, you still need room for functions to put their frames on it.
You may want to try and use Boost.Multi_array
typedef boost::multi_array<double, 2> Double2d;
Double2d x(boost::extents[5000][500]);
Double2d y(boost::extents[5000][500]);
Double2d z(boost::extents[5000][500]);
The actual large memory chunk will be allocated on the heap and automatically deallocated when necessary.
Your declaration should appear at top level, outside any procedure or method.
By far the easiest way to diagnose a segfault in C or C++ code is to use valgrind. If one of your arrays is at fault, valgrind will pinpoint exactly where and how. If the fault lies elsewhere, it will tell you that, too.
valgrind can be used on any x86 binary but will give more information if you compile with gcc -g.
One reservation about always using vector: as far as I understand it, if you walk off the end of the array it just allocates a larger array and copies everything over which might create subtle and hard to find errors when you are really tying to work with a fixed size array. At least with a real array you'll segfault if you walk off the end making the error easier to catch.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
typedef double (*array5k_t)[5000];
array5k_t array5k = calloc(5000, sizeof(double)*5000);
// should generate segfault error
array5k[5000][5001] = 10;
return 0;
}
Looks to me like you have an honest-to-Spolsky stack overflow!
Try compiling your program with gcc's -fstack-check option. If your arrays are too big to allocate on the stack, you'll get a StorageError exception.
I think it's a good bet, though, as 5000*500*3 doubles (8 bytes each) comes to around 60 megs - no platform has enough stack for that. You'll have to allocate your big arrays on the heap.
Another solution to the previous ones would be to execute a
ulimit -s stack_area
to expand the maximum stack.
You may want to try the Multi library for multidimensional arrays (C++17).
#include<multi/array.hpp>
#include<cassert>
namespace multi = boost::multi;
int main() {
using Double2D = multi::array<double, 2>;
Double2D X({5000, 500}, 999.0);
Double2D Y({5000, 500});
Double2D Z({5000, 500});
assert( X.size() == 5000 );
auto [m, n] = X.extensions();
assert( m == 5000 );
assert( n == 500 );
Y = X;
assert( Y[0][0] == 999.0 );
}
https://godbolt.org/z/rh5M463Y1
Similarly to the Boost.MultiArray library (other answer), allocates memory in the heap instead of using (overflowing) the stack.
It provides other features, like assignment and iteration.