What is the best way to handle dynamic multidimensional arrays in C / C ++?

What is an acceptable / most commonly used way to manipulate dynamic (with all parameters unknown before execution) multidimensional arrays in C and / or C ++.

I am trying to find the cleanest way to accomplish what this Java code does:

public static void main(String[] args){ Scanner sc=new Scanner(System.in); int rows=sc.nextInt(); int cols=sc.nextInt(); int[][] data=new int[rows][cols]; manipulate(data); } public static void manipulate(int[][] data){ for(int i=0;i<data.length;i++) for(int j=0;j<data[0].length.j++){ System.out.print(data[i][j]); } } 

(read from std_in to clarify that sizes are unknown before execution).

Edit: I noticed that this question is quite popular, although it is quite old. In fact, I do not agree with the higher vote. I think the best choice for C is to use a one-dimensional array, as I said below: "You can select rowscolssizeof (int) and access it by the [row * cols + col] table."

There are a number of options with C ++, if you really like boost or stl, then the answers below may be preferable, but the simplest and probably the fastest choice is to use a one-dimensional array, as in C.

Another viable choice in C and C ++, if you want the syntax [] [] to be the lingual answer below, manually creating an array with lots of malloc.

+23
c ++ c arrays multidimensional-array
Dec 13 '08 at 21:38
source share
10 answers

Use boost :: multi_array .

As in your example, the only thing you need to know at compile time is the number of dimensions. Here is the first example in the documentation:

 #include "boost/multi_array.hpp" #include <cassert> int main () { // Create a 3D array that is 3 x 4 x 2 typedef boost::multi_array<double, 3> array_type; typedef array_type::index index; array_type A(boost::extents[3][4][2]); // Assign values to the elements int values = 0; for(index i = 0; i != 3; ++i) for(index j = 0; j != 4; ++j) for(index k = 0; k != 2; ++k) A[i][j][k] = values++; // Verify values int verify = 0; for(index i = 0; i != 3; ++i) for(index j = 0; j != 4; ++j) for(index k = 0; k != 2; ++k) assert(A[i][j][k] == verify++); return 0; } 

Edit: as suggested in the comments, here is an example of a โ€œsimpleโ€ example that allows you to determine the size of a multidimensional array at run time by asking the console entry. Here is a sample output from this sample application (compiled with a constant denoting its 3 dimensions):

 Multi-Array test! Please enter the size of the dimension 0 : 4 Please enter the size of the dimension 1 : 6 Please enter the size of the dimension 2 : 2 Text matrix with 3 dimensions of size (4,6,2) have been created. Ready! Type 'help' for the command list. >read 0.0.0 Text at (0,0,0) : "" >write 0.0.0 "This is a nice test!" Text "This is a nice test!" written at position (0,0,0) >read 0.0.0 Text at (0,0,0) : "This is a nice test!" >write 0,0,1 "What a nice day!" Text "What a nice day!" written at position (0,0,1) >read 0.0.0 Text at (0,0,0) : "This is a nice test!" >read 0.0.1 Text at (0,0,1) : "What a nice day!" >write 3,5,1 "This is the last text!" Text "This is the last text!" written at position (3,5,1) >read 3,5,1 Text at (3,5,1) : "This is the last text!" >exit 

Important parts of the code are the main function, where we get the sizes from the user and create an array with:

 const unsigned int DIMENSION_COUNT = 3; // dimension count for this test application, change it at will :) // here is the type of the multi-dimensional (DIMENSION_COUNT dimensions here) array we want to use // for this example, it own texts typedef boost::multi_array< std::string , DIMENSION_COUNT > TextMatrix; // this provide size/index based position for a TextMatrix entry. typedef std::tr1::array<TextMatrix::index, DIMENSION_COUNT> Position; // note that it can be a boost::array or a simple array /* This function will allow the user to manipulate the created array by managing it commands. Returns true if the exit command have been called. */ bool process_command( const std::string& entry, TextMatrix& text_matrix ); /* Print the position values in the standard output. */ void display_position( const Position& position ); int main() { std::cout << "Multi-Array test!" << std::endl; // get the dimension informations from the user Position dimensions; // this array will hold the size of each dimension for( int dimension_idx = 0; dimension_idx < DIMENSION_COUNT; ++dimension_idx ) { std::cout << "Please enter the size of the dimension "<< dimension_idx <<" : "; // note that here we should check the type of the entry, but it a simple example so lets assume we take good numbers std::cin >> dimensions[dimension_idx]; std::cout << std::endl; } // now create the multi-dimensional array with the previously collected informations TextMatrix text_matrix( dimensions ); std::cout << "Text matrix with " << DIMENSION_COUNT << " dimensions of size "; display_position( dimensions ); std::cout << " have been created."<< std::endl; std::cout << std::endl; std::cout << "Ready!" << std::endl; std::cout << "Type 'help' for the command list." << std::endl; std::cin.sync(); // we can now play with it as long as we want bool wants_to_exit = false; while( !wants_to_exit ) { std::cout << std::endl << ">" ; std::tr1::array< char, 256 > entry_buffer; std::cin.getline(entry_buffer.data(), entry_buffer.size()); const std::string entry( entry_buffer.data() ); wants_to_exit = process_command( entry, text_matrix ); } return 0; } 

And you can see that to attach an element in an array is very simple: you just use the () operator, as in the following functions:

 void write_in_text_matrix( TextMatrix& text_matrix, const Position& position, const std::string& text ) { text_matrix( position ) = text; std::cout << "Text \"" << text << "\" written at position "; display_position( position ); std::cout << std::endl; } void read_from_text_matrix( const TextMatrix& text_matrix, const Position& position ) { const std::string& text = text_matrix( position ); std::cout << "Text at "; display_position(position); std::cout << " : "<< std::endl; std::cout << " \"" << text << "\"" << std::endl; } 

Note. I compiled this application in VC9 + SP1 - I received only some forgotten warnings.

+20
Dec 13 '08 at 21:52
source share

There are two ways to represent a 2-dimensional array in C ++. One of them is more flexible than the other.

Array of arrays

First create an array of pointers, then initialize each pointer with a different array.

 // First dimension int** array = new int*[3]; for( int i = 0; i < 3; ++i ) { // Second dimension array[i] = new int[4]; } // You can then access your array data with for( int i = 0; i < 3; ++i ) { for( int j = 0; j < 4; ++j ) { std::cout << array[i][j]; } } 

The problem with this method is that as many arrays are allocated to your second dimension that it does not facilitate the work of the memory allocator. Your memory will probably be fragmented, which will lead to performance degradation. This provides more flexibility, since each array in the second dimension can have a different size.

Large array to store all values

The trick here is to create a massive array to store all the necessary data. The tough part is that you still need the first array of pointers if you want to access the data using the syntax [i] [j].

 int* buffer = new int[3*4]; int** array = new int*[3]; for( int i = 0; i < 3; ++i ) { array[i] = array + i * 4; } 

The int * array is optional, since you can access your data directly in the buffer by calculating the index in the buffer from the two-dimensional coordinates of the value.

 // You can then access your array data with for( int i = 0; i < 3; ++i ) { for( int j = 0; j < 4; ++j ) { const int index = i * 4 + j; std::cout << buffer[index]; } } 

RULE to keep in mind

Computer memory is linear and will last a long time. Keep in mind that 2-dimensional arrays are not supported on the computer, so the only way is to "linearize" the array into a 1-dimensional array.

+7
Dec 14 '08 at 1:59
source share

You can select rowscolssizeof (int) and access it by the [row * cols + col] table.

+5
Dec 13 '08 at 21:44
source share

The standard way without using boost is to use std :: vector:

 std::vector< std::vector<int> > v; v.resize(rows, std::vector<int>(cols, 42)); // init value is 42 v[row][col] = ...; 

This will take care of the new / deletion of the memory you need. But it is rather slow since std::vector not intended primarily for its use (embedding std::vector in each other). For example, all memory is not allocated in one block, but is separate for each column. In addition, the lines do not have to be the same width. The faster one uses a regular vector, and then performs an index calculation, for example col_count * row + col , to get a specific row and col:

 std::vector<int> v(col_count * row_count, 42); v[col_count * row + col) = ...; 

But this will lose the ability to index the vector using [x][y] . You should also store the number of rows and columns somewhere, while with the nested solution you can get the number of rows using v.size() , and the number of cols using v[0].size() .

Using boost, you can use boost::multi_array , which does exactly what you want (see another answer).




There is also a source path using native C ++ arrays. This provides some work and is in no way better than an embedded vector solution:

 int ** rows = new int*[row_count]; for(std::size_t i = 0; i < row_count; i++) { rows[i] = new int[cols_count]; std::fill(rows[i], rows[i] + cols_count, 42); } // use it... rows[row][col] then free it... for(std::size_t i = 0; i < row_count; i++) { delete[] rows[i]; } delete[] rows; 

You need to keep the number of columns and rows that you created somewhere, since you cannot get them from the pointer.

+4
Dec 13 '08 at 22:17
source share

Here is an easy way to do this in C:

 void manipulate(int rows, int cols, int (*data)[cols]) { for(int i=0; i < rows; i++) { for(int j=0; j < cols; j++) { printf("%d ", data[i][j]); } printf("\n"); } } int main() { int rows = ...; int cols = ...; int (*data)[cols] = malloc(rows*sizeof(*data)); manipulate(rows, cols, data); free(data); } 

This works fine with C99, however it is not C ++ of any standard: C ++ requires that the sizes of the array types be compilation constants. In this regard, C ++ is now fifteen years behind C. And this situation will not change soon (the offer of a variable-length array for C ++ 17 does not come close to the functionality of variable-length arrays C99).

+4
Jan 16 '15 at 6:34
source share

C-style 2D arrays in C and C ++ are a memory block of size rows * columns * sizeof(datatype) bytes.

Actual [row] [column] sizes exist only statically at compile time. There is nothing dynamic at runtime!

So, as others have noted, you can implement

  int array [ rows ] [ columns ]; 

how

  int array [ rows * columns ] 

Or how:

  int * array = malloc ( rows * columns * sizeof(int) ); 



Next: Declaring an array with a variable size. In C, this is possible:

 int main( int argc, char ** argv ) { assert( argc > 2 ); int rows = atoi( argv[1] ); int columns = atoi( argv[2] ); assert(rows > 0 && columns > 0); int data [ rows ] [ columns ]; // Yes, legal! memset( &data, 0, sizeof(data) ); print( rows, columns, data ); manipulate( rows, columns, data ); print( rows, columns, data ); } 



In C, you can simply pass in a variable-sized array in much the same way as a variable-sized array:

 void manipulate( int theRows, int theColumns, int theData[theRows][theColumns] ) { for ( int r = 0; r < theRows; r ++ ) for ( int c = 0; c < theColumns; c ++ ) theData[r][c] = r*10 + c; } 

However, in C ++ this is not possible. You must allocate an array using dynamic allocation, for example:

 int *array = new int[rows * cols](); 

or preferably (with automatic memory management)

 std::vector<int> array(rows * cols); 

Then the functions must be changed to accept one-dimensional data:

 void manipulate( int theRows, int theColumns, int *theData ) { for ( int r = 0; r < theRows; r ++ ) for ( int c = 0; c < theColumns; c ++ ) theData[r * theColumns + c] = r*10 + c; } 
+3
Dec 14 '08 at 0:31
source share

If you use C instead of C ++, you can look at the Array_T abstraction in the Dave Hanson C library Interfaces and Implementations . It is exceptionally clean and well designed. I have my students doing a two-dimensional version as an exercise. You could do this or just write an additional function that performs index conversion, for example,

 void *Array_get_2d(Array_T a, int width, int height, int i, int j) { return Array_get(a, j * width, i, j); } 

It's a little cleaner to have a separate structure in which you store the width, height and pointer to the elements.

+2
Dec 13 '08 at 22:13
source share

I recently encountered a similar problem. I did not have a boost. The vectors of vectors turned out to be rather slow compared to conventional arrays. Having an array of pointers makes initialization a lot more time consuming because you need to iterate over each dimension and initialize the pointers, possibly having some rather bulky, cascading types in the process, possibly with a lot of typedef.

DISCLAIMER: I was not sure if I should post this as an answer, because it answers only part of your question. I apologize for the following:

  • I did not consider how to read measurements from standard input, as other commentators noted.
  • This is primarily for C ++.
  • I just coded this solution for two dimensions.

I decided to publish this anyway, because I see vectors of vectors that often arise in response to questions about multidimensional arrays in C ++, without mentioning performance aspects (if you're interested).

I also interpreted the main problem of this question as how to get dynamic multidimensional arrays that can be used with the same ease as the Java example from the question, i.e. no hassle when calculating indices with a pseudo-multidimensional one-dimensional array.

I have not seen the compiler extensions mentioned in other answers like GCC / g ++ to declare multidimensional arrays with dynamic borders in the same way as with static borders. As far as I understand, the question does not limit the answers to standard C / C ++. The ISO C99 seems to support them, but in C ++ and previous versions of C, they seem to be compiler specific extensions. See This question: Dynamic arrays in C without malloc?

I came up with a way that people might like it in C ++ because it is small code, has the ease of using built-in static multidimensional arrays, and just as fast.

 template <typename T> class Array2D { private: std::unique_ptr<T> managed_array_; T* array_; size_t x_, y_; public: Array2D(size_t x, size_t y) { managed_array_.reset(new T[x * y]); array_ = managed_array_.get(); y_ = y; } T* operator[](size_t x) const { return &array_[x * y_]; } }; 

You can use it like that. Size not

 auto a = Array2D<int>(x, y); a[xi][yi] = 42; 

You can add a statement to at least all but the last dimension and extend the idea to more than two dimensions. I made a blog post about alternative ways to get multidimensional arrays. I am also more specific about relative performance and coding there.

Performance of dynamic multidimensional arrays in C ++

+1
Jan 16 '15 at 6:16
source share

It is not possible to determine the length of a given array in C ++. The best way would probably be to pass the length of each dimension of the array and use this instead of the .length property of the array itself.

0
Dec 13 '08 at 21:43
source share

You can use malloc to accomplish this and still have access to it using a regular array [] [], which means the verses of the array method [rows * cols + cols].

 main() { int i; int rows; int cols; int **array = NULL; array = malloc(sizeof(int*) * rows); if (array == NULL) return 0; // check for malloc fail for (i = 0; i < rows; i++) { array[i] = malloc(sizeof(int) * cols) if (array[i] == NULL) return 0; // check for malloc fail } // and now you have a dynamically sized array } 
0
Dec 14 '08 at 17:16
source share



All Articles