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
}