C++/Variable lenght matrix
Expert: Ralph McArdell - 7/18/2011
QuestionHi Ralph
How I can use two dim array (matrix) with variable length in c++?
I used this:
int * c=(int*)malloc(sizeof(int)*n*n);
But it doesn,t work in vs2008.
Thanks in advance.
Best Regards.
Sara
AnswerMy first thought is why are you even contemplating using malloc for dynamic object allocation in C++?
In C++ new and new [] are the dynamic object creation operators, together with delete and delete [] for dynamic object destruction.
However, given that, if you surround the code you posted with the correct support code then it should compile:
- include declarations for the (C) library malloc, free, etc. function.
In C++ the header is cstdlib, in C it is stdlib.h.
- define n.
Remember in C malloc and free are _library functions_ and _not_ language operators as new, new[], delete and delete [] are in C++. Also, unlike C, C++ _requires_ that the compiler has seem a declaration for a function before its first use (i.e. before the first call to that function).
The following build in Visual Studio 2010 using Visual C++ 2010:
#include <cstdlib>
int main()
{
int n(10);
int * c=(int*)std::malloc(sizeof(int)*n*n);
std::free(c);
}
Note also that properly in C++ pretty much all (non-preprocessor-macro) standard C++ library names are in the std namespace, even those C++ inherited from the C standard library, although malloc and free without being qualified with std would still work.
In C++ you could just have written:
int main()
{
int n(10);
int * c = new int [n*n];
delete [] c;
}
Because we are creating and destroying multiple objects we have to use the array forms of new and delete - new [] and delete [].
There are several things to note about the new and delete operators (and their array counterparts):
- they work using the type of the object(s) created, hence
+ there is no need to specify size of the object type in new expressions
+ new returns a pointer to the type created, and new [] a pointer to the first object created,
there is no need to cast the returned pointer from new from void
- unlike malloc etc. which only allocate raw memory, new really does create objects:
+ first raw memory is allocated
+ then one object (or probably more for new []) is constructed in this raw memory
- unlike free which only releases raw memory, delete really does destroy objects:
+ first the destructor for the object (or for each object for delete []) is called
+ then the raw memory used by the object or objects is released
Unfortunately new [] has the same limitations as malloc on creating dynamic arrays which have multiple dimensions that vary at runtime - it can only create a vector (single dimension array) of objects dynamically, however if all other dimensions are fixed then we can create a dynamically sized vector of fixed sized vectors or arrays. This comes from the way C and C++ support for multidimension arrays. A 2D array of elements of type T is a vector of vectors-of-T, a 3D array of elements of type T is a vector of vectors-of-vectors-of-T, etc. Hence we can have a dynamically allocated vector whose elements are fixed sized vectors of T or fixed sized vectors of fixed sized vectors of T etc.:
int main()
{
// 2D array with one dimension having fixed size and the other specified at runtime:
// Elements of the dynamic size are in fact each a vector of fixed size:
int (*m2d)[10] = new int[n][10];
delete [] m2d;
// 3D array with two dimensions having fixed size and the third specified at runtime:
// Elements of the dynamic size are in fact each a vector of fixed sized vectors of fixed size:
int (*m3d)[10][10] = new int[n][10][10];
delete [] m3d;
}
The need for the parenthesis in the type is to ensure m2d and m3d are pointers to arrays of int rather than arrays of pointers to int.
However in your case you seem to require both dimensions to vary at runtime so you have to use a form of new [] similar to your initial use of malloc. C++ does have features that can help, primarily classes. For example if you do not mind loosing the c[a][b] syntax then we could wrap the array in a class:
class TwoDIntArray
{
public:
TwoDIntArray(size_t rows, size_t columns)
: rows_(rows)
, columns_(columns)
, elements_( new int [rows * columns] )
{
}
~TwoDIntArray()
{
delete [] elements_;
}
int & at( size_t r, size_t c )
{
return elements_[columns_*r + c];
}
int at( size_t r, size_t c ) const
{
return elements_[columns_*r + c];
}
private:
size_t rows_;
size_t columns_;
int * elements_;
};
In fact although the number of rows is stored for each TwoDIntArray object it is not required in this simple implementation. However if you added features - e.g. range checking - then the row_ member would be useful. Oh, and technically size_t (std::size_t) is defined in the header cstddef (again!), however some compilers will now automatically assume certain common library entities are already declared or defined - Visual C++ is one of those (and I think this includes the 2008 edition) that does this for size_t - BUT seems not to for std::size_t unless you actually #include <cstddef>.
The example TwoDIntArray class is very simple (in fact it is too simple - as copy and assignment behaviour have not been addressed and copying or assigning one TwoDIntArray object to another will lead to multiple TwoDIntArray referencing the same array and - on assignment - arrays becoming orphaned in memory - fixing this is left as an exercise).
You create a TwoDIntArray like so:
TwoDIntArray ia2d(10,10);
This creates an object on the stack (assuming the definition appears in a function body) which contains the row and column dimensions and a pointer to the dynamically created vector of row * column int objects. When ia2d goes out of scope (e.g. at the end of the function body the ia2d definition appears in) its destructor is called which deletes all the int objects (which is a do-nothing operation for int type objects) and releases the raw memory they used.
We can access the elements using the at member functions. For example to initialise all elements to 0 we could write:
for ( size_t r=0; r < 10; ++r )
{
for ( size_t c=0; c<10; ++c )
{
ia2d.at(r, c) = 0;
}
}
This uses the at overload that return a reference to the element requested, which means we can write to it as well as read it - obviously not a use for constant access (i.e. read only access) to the object as it modifies the array of ints managed by the TwoDIntArray object and would preclude using constant TwoDIntArray objects. To allow this the second at overload is provided. This member function at overload is qualified as const - i.e. calling this member function on a TwoDIntArray object will not modify that object. The only difference to the non-const at member function is that it returns the selected element by value - i.e. a copy is made of the int element, which, as it is totally independent to the element it was copied from, if modified will not affect the array of ints.
The above scheme can be easily extended to more than 2 dimensions.
For the 2D case however we can in fact easily provide the familiar c[a][b] syntax. The array in a class that provides a [] operator that returns a pointer to the first int in a whole row rather than a reference to or copy of an element. This works because of the close relationship in C and C++ between pointers and built in arrays and the fact the pointer[i] syntax is equivalent to *(pointer+i), so on returning a pointer to the start of a row placing an additional [b] after the wrapper-class-supported [a] will access the bth element from the start of the row. It will not work so easily with higher dimension arrays because then we would have to return a pointer to the start of a chunk of memory representing a multidimensional sub array and the compiler will require knowledge of the shape of this sub array (e.g. for a wrapped 3D array we would return a pointer to the start of a chunk of memory representing a 2D sub array; if such a sub array contained say 20 elements is that 2 rows of 10 columns, 10 rows of 2 columns, 4 rows of 5 columns or 5 rows of 4 columns?). Such cases could possibly be handled by returning some sort of object of a (sub-)array class type.
For the 2D case we could try something like so:
class TwoDIntArray
{
public:
TwoDIntArray(std::size_t rows, size_t columns)
: rows_(rows)
, columns_(columns)
, elements_( new int [rows * columns] )
{
}
~TwoDIntArray()
{
delete [] elements_;
}
// Return pointer to mutable int that is start of a row of ints
// in the managed 2D array
int * operator[]( size_t r )
{
return elements_+columns_*r;
}
// Return pointer to constant int that is start of a row of ints
// in the managed 2D array
int const * operator[]( size_t r ) const
{
return elements_+columns_*r;
}
private:
size_t rows_;
size_t columns_;
int * elements_;
};
The example usage would then look like so:
TwoDIntArray ia2d(10,10);
for ( size_t r=0; r < 10; ++r )
{
for ( size_t c=0; c<10; ++c )
{
ia2d[r][c] = 0;
}
}
Hope this help with your immediate problem and gives you some ideas how to make better use of C++ to help make your code clearer and easier to work with.