La bla bla
La bla bla

Reputation: 8718

copying multidimensional array (pointers)

I have two structs pointers to pointers

typedef struct Square {
...
...
}Square;

Square **s1; //Representing 2D array of say, 100*100
Square **s2; //Representing 2D array of say, 200*200

Both are allocated on the heap using malloc(). I have s1 initialized with certain values and s2 initialized completely with the default values. Basically I need to resize s1 to the size of s2 while maintaining its (s1) values, and the 'added' values would be just as they were in s2 - the default value.

I wrote this question memcpy() from smaller array to larger one but apparently I'm confusing between arrays and pointers/

My question is, how to implement this resizing of s1 to the size of s2. I don't have to keep the original pointer. I can copy s1 to s2 and return s2 if that's a better way I hope I explained what I'm after properly. Thanks!

Upvotes: 0

Views: 5444

Answers (3)

sfstewman
sfstewman

Reputation: 5687

You've allocated your 2-D matrix on the heap, and you're using a Square** to access it. This means that you've: (1) allocated the space for each element in one or more calls to malloc, and (2) allocated the space for all of the row pointers in a call to malloc. How to proceed depends quite a lot of how you've allocated the array.

Below, I use assert to stress that each malloc/realloc can return NULL (indicating that it could not complete the request). You'll probably want to handle these cases properly.

Option 1: You allocated each row separately

You allocated the s1 matrix like this:

Square** s1 = malloc(M1*sizeof(s1[0]));
for (size_t i=0; i < M1; i++)
  s1[i] = malloc(N1*sizeof(s1[i][0]));

In this case, you have to handle each row separately:

/* M1 and N1 set to size of s1 (M1 x N1) */
/* M2 and N2 set to size of s2 (M2 x N2) */

/* First, reallocate the pointers to each row */
Square** tmpRows = realloc(s1, M2*sizeof(*tmpRows));
assert( (tmpRows != NULL) && "Out of memory reallocating rows" );

s1 = tmpRows;

/* Now, reallocate each row */
for (size_t i=0; i < M1; i++) {
  Square* tmpVals = realloc(s1[i], N2*sizeof(tmpVals[0]));
  assert( (tmpVals != NULL) && "Out of memory reallocating row" );

  /* copy elements of s2 into new column */
  memcpy(tmpVals+N1, s2[i]+N1, (N2-N1)*sizeof(s1[i][0]));
  s1[i] = tmpVals;
}

/* Now, allocate each new row of s1 and copy the additional rows of s2 into s1 */
for (size_t i=M1; i < M2; i++) {
  s1[i] = malloc( N2 * sizeof(s1[i][0]) );
  assert( (s1[i] != NULL) && "Out of memory allocating new row" );
  memcpy(s1[i], s2[i], N2*sizeof(s1[i][0]));
}

Option 2: You allocated each all of the rows at once

In this case, you allocated all of the rows in one big chunk, and then assigned pointers to the beginning of each row. Like this:

Square** s1 = malloc(M1*sizeof(s1[0]));
s1[0] = malloc( M1*N1*sizeof(s1[0][0]) );
for(size_t i=1; i < M1; i++) 
  s1[i] = s1[i-1] + N1;

To resize the array (and initialize its new elements with those of s2), you should do the following:

/* M1 and N1 set to size of s1 (M1 x N1) */
/* M2 and N2 set to size of s2 (M2 x N2) */

/* Make a new copy of the elements of s1.  Linear layout of a 200x200 
 * matrix will be different than the linear layout of a 100x100 matrix.
 * Making a new copy makes it easier to initialize its values.
 */
Square* new_mat = malloc( M2*N2*sizeof(new_mat[0]) );
assert( (new_mat != NULL) && "Out of memory allocating new matrix" );

/* Initialize with values of s2.  Assumption: s2 is also allocated
 * as a contiguous array...
 */
memcpy(new_mat, s2[0], M2*N2*sizeof(s2[0][0]));

/* Now, reallocate the rows */
Square** tmpRows = realloc(s1, M2*sizeof(s1[0]));
assert( (tmpRows != NULL) && "Out of memory reallocating rows" );

s1 = tmpRows;
/* Copy data from old rows into new rows... */
for (size_t i=0; i < M1; i++) {
  /* rows of s1 still point to old_mat data, copy it into new_mat.
   * Each row in new_mat starts at (new_mat + N2*i)
   */
  memcpy( new_mat + N2*i, s1[i], N1*sizeof(s1[i][0]) );
}

/* Free old memory and assign new row pointers... */
free(s1[0]);
s1[0] = new_mat;
for (size_t i=1; i < M2; i++)
  s1[i] = s1[i-1] + N2;

Upvotes: 1

Clark Gaebel
Clark Gaebel

Reputation: 17988

You can try something like this:

typedef struct {
    //
} Square;

Square** s1; // 100x100, needs to resize and be like s2.
Square** s2; // 200x200

void resize_s1()
{
    // resize the outer array
    s1 = realloc(s1, sizeof(Square*)*200);
    memset(s1 + 100, 0, 100*sizeof(Square*)); // we initialize the newly allocated pointers to NULL

    for(int i = 0; i < 200; ++i)
    {
        // resize the inner array. Since we initialized the last
        // 100 pointers to null, realloc will just behave like
        // malloc for them.
        s1[i] = realloc(s1[i], 200*sizeof(Square));

        // ... and copy the new values in! You can omit this step,
        // but anything outside of the original bounds of s1 will
        // be uninitialized. All your pointers will be valid though.
        if(i >= 100)
            memcpy(s1[i] + 100, s2[i] + 100, 100*sizeof(Square));
    }
}

As a word of warning - I'm playing very fast and loose with realloc here. Read its man page for more details, but if you ever hit low memory conditions, bad things can happen.

Upvotes: 1

anttix
anttix

Reputation: 7789

Two dimensional arrays are laid out in memory sequentially: row1 row2 row3 etc.

memcpy does a linear copy from one memory location to another.

So to achieve what you need:

a) Create a new array

Square **s3 = malloc(sizeof(s2));

b) Copy s2 into it

c) Copy stuff from s1, row by row into new

for(r = 0; r < NROWS_S1; r++)
    memcpy(s3[r], s1[r], sizeof(Square) * NCOLS_S1);

http://www.fredosaurus.com/notes-cpp/arrayptr/23two-dim-array-memory-layout.html

Upvotes: 1

Related Questions