You are here:

C++/Variable lenght matrix

Advertisement


Question
Hi 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

Answer
My 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.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.