-5

I have a dynamic two - dimensional array , size is height*width.

I want to change array size by using realloc.

This is my code but it is not working.

What should I do? Help me!!

int main(){
      char **img;
      img = (char**)malloc(sizeof(char*)*height);

      for(i=0;i<height;i++){
        img[i] = (char*) malloc(sizeof(char)*width);
      }

      resize(height*2, width*2, img);
}

void resize(int height, int width, char **img){
      int i;
      img = (char**)realloc(img, sizeof(char)*height);
      for(i=0;i<h;i++){
         img[i] = (char*)realloc(img[i], width);
      }

}
조승윤
  • 17
  • 3
  • To begin with, I recommend you do some research about *emulating pass by reference in c*. To continue, what will the *new* elements of `img` be after the reallocation? Hint: they are *uninitialized* and can't be passed as is to `realloc`. Lastly, you should never assign back to the pointer you pass to `realloc`, in case `realloc` fails. – Some programmer dude Oct 09 '17 at 07:28
  • This is **not** a 2d array. And the logic is flawed, first `free()` individual arrays that are no longer needed (if any), then resize your array of pointers, then allocate newly needed individual arrays and resize those that were there before. And of course, check for errors on **each** `malloc()` / `realloc()` call. Finally, modifying a local variable modifies a **copy**, you have to e.g. return `img` when you're done. –  Oct 09 '17 at 07:29
  • 3
    And in general, "*but it is not working.*" is **not** a suitable problem description. –  Oct 09 '17 at 07:32
  • `h` is not defined anywhere – M.M Oct 09 '17 at 07:32
  • [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) – Lundin Oct 09 '17 at 09:53
  • @Lundin if resizing in more than one dimension should work preserving the contents, a real multi-dimensional array won't fit the bill. –  Oct 09 '17 at 10:04
  • @FelixPalmen No, but the OP has a misconception that their code is allocating 2D arrays. – Lundin Oct 09 '17 at 10:44

3 Answers3

1

There are two main issues. First, realloc may move the memory block to a new position. Therefore realloc has a return value pointing to either the "old" memory block if no move were necessary, to a new block if a move were necessary, or NULL if an error occurred. Yet you neglect this fact in resize, as it cannot change the pointer object a caller passes in. I'd suggest to adapt the prototype such that resize returns a (probably new) pointer just like realloc does.

Second, when reallocating each row, there might be uninitialized values in the array, probably pointing to "somewhere". Reallocating such an uninitialized value is undefined behaviour. I'd suggest to set the "new" rows to NULL such that realloc can behave correctly afterwards. Therefore it is necessary to know the "old" height, since you have no chance otherwise to distinguish a regularly initialized pointer from a "garbage" pointer.

See the adapted code. Hope it helps.

char** resize(int oldHeight, int oldWidth, int height, int width, char **img){
    int i;
    img = realloc(img, sizeof(char)*height);
    for (int i=oldHeight; i<height; i++)
        img[i] = NULL;

    for(i=0;i<height;i++){
        img[i] = realloc(img[i], width);
        for (int col=oldWidth; col < width; col++) {
            img[i][col] = 0;
        }
    }

    return img;
}

int main(){
    int height = 10;
    int width = 20;

    char **img;
    img = malloc(sizeof(char*)*height);

    for(int i=0;i<height;i++){
        img[i] = malloc(sizeof(char)*width);
    }

    img = resize(height, width, height*2, width*2, img);
}
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
0

Besides you

  • are not dealing with a 2D-array and
  • and the code leak memory if the new height is smaller then the old height and
  • you do not need to cast void-pointers in C and
  • all ints should be size_ts

there are two major bugs here, as the code

  • passes the wrong size to reallocate the outer array. It multiplies height by sizeof (char) instead of sizeof (char*).
  • misses to initialise the additional pointers realloced to the "outer" array with NULL before passing them to realloc() via the inner resizing-loop.

So the minimal adjustments assuming the new height is greater or equal the old height could look like

void resize(size_t height, size_t height_current, size_t width, char **img){
  int i;
  img = (char**)realloc(img, sizeof(char*)*height);
  for(i=height_current;i<height;i++){
     img[i] = NULL;
  }
  for(i=0;i<width;i++){ // correct copypasta mistake here
     img[i] = (char*)realloc(img[i], width); 
  }
}

A nicer version could look like this

void resize(size_t height, size_t height_current, size_t width, size_t char **img)
{
  if (height != height_current)
  {
    if (height < height_current)
    {
      for (size_t i = height; i < height_current; ++i)
      {
         free(img[i]);
      }
    }

    img = realloc(img, height * sizeof *img);

    if (height > height_current)
    {
      for (size_t i = height_current; i < height; ++i)
      {
         img[i] = NULL;
      }
    }
  }

  for (size_t i = 0; i < width; ++i)
  {
     img[i] = realloc(img[i], width * sizeof *img[i]);
  }
}

Call it like this:

resize(height*2, height, width*2, img);

Also you really want to add error checking to all calls to malloc() and realloc() as they might very well fail!

alk
  • 69,737
  • 10
  • 105
  • 255
0

To do this correctly, you must know at least the number of rows before resizing. One possibility is to define a struct containing additional information (kind of OOP approach, have all relevant data together), like the following example (also storing the number of columns, just for completeness, untested code here):

#include <stdlib.h>
#include <string.h>

typedef struct Lookup Lookup;

struct Lookup
{
    size_t rows;
    size_t cols;
    char **data;
};

static void Lookup_destroy(Lookup *self)
{
    if (!self) return;
    for (size_t r = 0; r < self->rows; ++r)
    {
        free(self->data[r]);
    }
    free(self->data);
    free(self);
}

static Lookup *Lookup_create(size_t rows, size_t cols)
{
    Lookup *self = malloc(sizeof *self);
    if (!self) return 0;

    self->rows = rows;
    self->cols = cols;
    self->data = malloc(rows * sizeof *(self->data));
    if (!self->data)
    {
        free(self);
        return 0;
    }
    memset(self->data, 0, rows * sizeof *(self->data));
    for (size_t r = 0; r < rows; ++r)
    {
        self->data[r] = malloc(cols * sizeof *(self->data[r]));
        if (!self->data[r])
        {
            Lookup_destroy(self);
            return 0;
        }
    }

    return self;
}

static Lookup *Lookup_resize(Lookup *self, size_t rows, size_t cols)
{
    if (!self) return Lookup_create(rows, cols);

    // free rows that are no longer needed, if any:
    for (size_t r = rows; r < self->rows; ++r)
    {
        free(self->data[r]);
        self->data[r] = 0;
    }

    // reallocate array of rows:
    char **newdata = realloc(self->data, rows * sizeof *newdata);
    if (!newdata)
    {
        Lookup_destroy(self);
        return 0;
    }

    // update row array and row count: 
    self->data = newdata;
    size_t oldrows = self->rows;
    self->rows = rows;

    // initialize new rows to NULL, if any:
    if (rows > oldrows)
    {
        memset(self->data + oldrows, 0,
                (rows - oldrows) * sizeof *(self->data));
    }

    // reallocate individual rows:
    for (size_t r = 0; r < rows; ++r)
    {
        char *newrow = realloc(self->data[r], cols * sizeof *newrow);
        if (!newrow)
        {
            Lookup_destroy(self);
            return 0;
        }
        self->data[r] = newrow;
    }

    // update col count:
    self->cols = cols;

    return self;
}

Note how the result of realloc() is always stored to a temporary variable first, this is needed to correctly handle errors. This code simply throws away the whole object in case of any error -- you can do different things with more code of course.

You could use it in your code like this:

int main(){
      Lookup img;
      img = Lookup_create(height, width);
      // check for NULL here

      img = Lookup_resize(img, height*2, width*2);
      // and check for NULL here
}