Is using realloc () in a dynamically allocated 2D array a good idea?

I am mainly interested in the viability of reducing such an array.

I am working on a project in which I used single calls to malloc () for each creation of separate mid-sized 2D arrays. (Only a few dozen MiBs, on the largest.) The fact is that during the life of one of the arrays its content sharply decreases (by more than half). Obviously, I could just leave the size of the array one for the whole life of the program. (This is only the exact MiB in a system with accessible gigabyte RAM.) But we are talking about the fact that more than half of the allocated space goes into unuse long before the program ends, and due to the way I use the array, all the saved data is stored in the adjacent rowset (at the beginning of the block). It seems like it's a waste to hold on to all this RAM if I really don't need it.

Although I know that realloc () can be used to compress dynamically created arrays, a 2D array is more complex. I think I understand the memory layout (how I implemented the function that structures it), but this pushes the limits of my understanding of the language and the work of its compilers. Obviously, I would have to work with strings (and deal with string pointers), and not just bytes, but I donโ€™t know how predictable the result of all this can be.

And yes, I need to create an array with one malloc (). The object in question has several million lines. I tried using the loop in malloc () each line separately, but the program always froze around 100,000 malloc () s.

For the background, the source that I use to build this array looks like this:

char ** alloc_2d_arr(int cnum, int rnum) { /* ((bytes for row pointers + (bytes for data)) */ char **mtx = malloc(rnum * sizeof (char *) + rnum * cnum * sizeof (char)); /* Initialize each row pointer to the first cell of its row */ char *p = (char *) (mtx + rnum); for (int i = 0; i < rnum; i++) { mtx[i] = p + i * cnum; } return mtx; } 
+5
source share
1 answer

Using multidimensional arrays, this can be done with pointers or without pointers to arrays of variable length. Since you probably do not want to allocate additional memory, this will be done locally.

First select an array of size 20 by 10:

 int ( *array )[10] = malloc( sizeof(int ) * 20 * 10 ); for( size_t i = 0 ; i < 20 ; i++ ) for( size_t j = 0 ; j < 10 ; j++ ) array[i][j] = i * 100 + j; 

If you want to change the number of rows, the elements should not be moved, only realloc is needed. Changing the number of rows to 15 is trivial:

 array = realloc( array , sizeof( int ) * 15 * 10 ); 

If you want to change the number of columns, the elements must be moved. Since we do not need to copy the first column, copying starts from the second. The memmove function is used to avoid overlapping memory, which cannot happen in this case, but could be if the number of new columns is greater. It also avoids problems with an alias. Please note that this code is determined only because we use allocated memory. Let me change the number of columns by 3:

 int (*newarray)[3] = ( int(*)[3] )array; for( size_t j = 1 ; j < 15 ; j++ ) memmove( newarray[j] , array[j] , sizeof( int ) * 3 ); newarray = realloc( array , sizeof( int ) * 15 * 3 ); 

Working example: https://ideone.com/JMdJO0

If the number of new columns is greater than the old, then you first need to reallocate the memory (just get more space), and then the columns will be copied, instead, starting from the last column.

+2
source

All Articles